# Pomerium Documentation > Curated Pomerium documentation with key topics inline. For the navigation > index, see [llms.txt](https://www.pomerium.com/llms.txt). For exhaustive page discovery, > see [llms-index.txt](https://www.pomerium.com/llms-index.txt). - Cite only current www.pomerium.com docs and markdown sidecars. Do not cite docs.pomerium.com or archive hosts. - Classify the management model before answering. Pomerium Core is the self-managed Pomerium server and data-plane engine for every deployment model. Choose Core when the user wants direct ownership of configuration, identity provider integration, TLS and DNS, storage, scaling, and operations. Use Core docs for Docker, Kubernetes, config.yaml, routes, policy, authenticate_service_url, and custom identity providers. - Pomerium Zero is Pomerium's managed control plane for Core clusters. Choose Zero when the user wants Pomerium-managed onboarding, configuration, starter domains, certificates, the Zero API, API users, service accounts, organizations, quotas, cluster health, and multi-replica scaling while Core runs in the user's environment. Use Zero docs for console.pomerium.app, the Zero API, cluster onboarding, cluster configuration, starter domains, certificates, and billing. - Pomerium Enterprise is the self-hosted control plane for Core clusters. Choose Enterprise when the user needs the control plane in their own environment plus the Enterprise Console, Enterprise API, centralized configuration, namespaces and RBAC, audit and deployment history, directory sync, external data, service accounts, sessions, metrics, and other governance, compliance, and scale capabilities. Use Enterprise docs for those management and operations features. - Do not answer Core configuration questions with Zero Console steps unless the user asks for Pomerium Zero. Do not describe Zero as a separate data plane; Zero manages Core deployments. - Prefer current PPL and reference pages for configuration questions. Use current route keys and policy syntax from the docs. - For group-based authorization questions, check the relevant IdP guide plus directory sync and JWT groups filter docs when groups are missing or too large. - For MCP questions, prefer the current MCP capability pages and reference docs over older guides or blog posts. ## Getting Started ### Pomerium Quickstart Source: https://www.pomerium.com/docs/get-started/quickstart.md # Pomerium Quickstart Get started by choosing how you want to manage Pomerium: Zero for a managed control plane, or Core when you want to own configuration and operations yourself. **Pomerium Zero:** Pomerium Zero is Pomerium's managed control plane for Core clusters. It connects to the Core cluster running in your environment and manages onboarding, configuration, starter domains, certificates, the Zero API, API users, service accounts, organizations, quotas, cluster health, and multi-replica scaling. ## Before you start - Install [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/) - Create a (free) [**Pomerium Zero account**](https://console.pomerium.app/create-account) ## Get Pomerium Zero configuration After you create an account, you'll be directed to the Zero onboarding screen. In the onboarding screen, select the **Docker** tab and copy the Docker Compose configuration. If you are using Pomerium Zero for the first time, but have an existing Pomerium Core configuration, you can import it into Pomerium Zero by following the prompts in the Zero onboarding flow. See [**Import Core Config to Zero**](https://www.pomerium.com/docs/deploy/cloud/import.md#how-to-import-your-core-configuration) for more information. (If you selected **Finish** before copying the Docker configuration, we've provided a copy below.) Paste the configuration in a `compose.yaml` file: ```yaml title="compose.yaml" {8,15} showLineNumbers services: pomerium: image: pomerium/pomerium:latest ports: - 443:443 restart: always environment: POMERIUM_ZERO_TOKEN: XDG_CACHE_HOME: /var/cache volumes: - pomerium-cache:/var/cache networks: main: aliases: - verify..pomerium.app verify: image: pomerium/verify:latest networks: main: aliases: - verify networks: main: {} volumes: pomerium-cache: ``` Replace the following values: - **Line 8**: Replace `CLUSTER_TOKEN` with the cluster token generated for you in the onboarding config - **Line 15**: Replace `CLUSTER_STARTER_SUBDOMAIN` with your generated starter subdomain You can find your **Starter Domain** at the top of the Zero Console navigation bar: \[Selecting the Cluster dropdown menu in the Zero Console to see the cluster starter domain] ## Connect to Pomerium Zero Next, deploy Pomerium with the following command: ```bash docker compose up -d ``` Pomerium will deploy and run in a special "Zero-managed" mode. In Zero-managed mode, Pomerium connects to the Pomerium Zero cloud service, a remote control plane where you can manage your deployments and configuration. If your Pomerium deployment connects to the cloud successfully, Pomerium Zero will notify you in the onboarding screen: \[The successful connection notification in the Pomerium Zero onboarding flow] Once you've connected, select **Finish**. You'll be directed to the Zero Console. If you can't connect to Pomerium Zero, check out these pages for help: - [**Configure port 443 to allow inbound access**](https://www.pomerium.com/docs/internals/troubleshooting.md#configure-port-443-to-allow-inbound-access) - [**Override IP address**](https://www.pomerium.com/docs/internals/clusters.md#detected-and-override-ip-address) ## Connect to upstream services In the Zero Console, we've provisioned two starter routes, **SSH** and **Verify**, that come with their own DNS records and TLS certificates: \[Viewing the two starter routes] ### Connect to Verify The first service you'll access is the **Verify** app. This hosted demo app is designed to verify a user's identity, but it also serves as a way to validate that Pomerium is up and running as expected. In the Zero Console, select the **From** URL for the Verify app. You should land on the Verify page: \[The homepage of the Verify app after the user is redirected] Check out [**Pomerium Fundamentals: Build Routes**](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-build-routes.md) to learn how to create a route to your own service behind Pomerium. ### Connect to SSH The demo SSH route connects to a remote server that you can use to test SSH connections in Pomerium Zero. This example uses **tunneled SSH**. For production deployments, consider [**Native SSH Access**](https://www.pomerium.com/docs/capabilities/native-ssh-access.md) which provides OAuth authentication with ephemeral certificates. You need the Pomerium CLI client to connect to services like **SSH** over a TCP connection. See the [**Pomerium CLI**](https://www.pomerium.com/docs/deploy/clients.md) page for installation instructions. In a terminal, enter the following command and replace `CLUSTER_STARTER_DOMAIN` with your own: ```bash pomerium-cli tcp ssh..app:22 ``` Initiate the SSH connection: ```bash ssh ssh-demo.pomerium.com -p 22 ``` A successful request results in a response similar to the one below: ```bash Warning: Permanently added 'ssh-demo.pomerium.com' (ED25519) to the list of known hosts. This is an SSH honeypot. Everything is logged and monitored. ``` ## Review starter policy and certificates Select the **Policies** tab. In the policies table, you'll notice a default policy that only grants access if the user's email matches the one associated with your Pomerium Zero account: \[Reviewing the starter policy in the Zero Console] This **Starter Policy** is assigned to both of the demo routes. You can remove this policy and create your own at any time. - Go to the [**PPL page**](https://www.pomerium.com/docs/internals/ppl.md) to learn about Pomerium Policy Language - Go to [**Build Policies**](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-build-policies.md) to learn how to create and assign policies using the Zero PPL builder Pomerium automatically provisions and manages TLS certificates issued by ZeroSSL to secure the starter routes in your cluster, and any routes you may build that use your cluster's starter domain. To see certificates in your cluster, go to the **Certificates** tab: \[View cluster certificates from ZeroSSL] ## Next Steps - [Build your First Route](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-build-routes.md) - [Add a Custom Domain](https://www.pomerium.com/docs/capabilities/custom-domains.md) **Pomerium Core:** Pomerium Core is the self-managed Pomerium server and data-plane engine. Choose Core when you want to install, configure, and operate Pomerium yourself. With Pomerium Core, you're assembling the pieces yourself. This quickstart assumes you have a publicly accessible domain with DNS you control. If you don't have those prerequisites ready, **[Pomerium Zero](https://console.pomerium.app/create-account)** provides a managed control plane with starter domains, certificates, and centralized configuration while Core still runs in your environment. You can move from Zero-managed configuration to self-managed configuration later if you want to own the control plane too. ## Before you start - A **publicly accessible server** with ports 443 and 80 available - A **domain** with DNS pointing to your server (e.g., `*.pomerium.yourdomain.com`) - [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/) installed This quickstart uses Docker, but Pomerium Core also supports [binaries, OS packages, and building from source](https://www.pomerium.com/docs/deploy/core.md). ## Set up your project Create a new directory for your Pomerium setup: ```bash mkdir pomerium_quickstart cd pomerium_quickstart ``` Your project will contain: - `config.yaml` - Pomerium configuration - `docker-compose.yaml` - Docker services configuration ## Configure Pomerium Core Create a `config.yaml` file with your Pomerium configuration: ```yaml title="config.yaml" # Pomerium will automatically obtain certificates from Let's Encrypt autocert: true # Routes routes: - from: https://verify.pomerium.yourdomain.com to: http://verify:8000 pass_identity_headers: true policy: - allow: or: - email: is: user@example.com ``` Replace: - `yourdomain.com` with your domain - `user@example.com` with your email address ## Create Docker Compose configuration Create a `docker-compose.yaml` file: ```yaml title="docker-compose.yaml" services: pomerium: image: pomerium/pomerium:latest volumes: - ./config.yaml:/pomerium/config.yaml:ro - pomerium-cache:/data/ ports: - '443:443' - '80:80' verify: image: pomerium/verify:latest volumes: pomerium-cache: ``` The `pomerium-cache` volume will store Let's Encrypt certificates so they persist across container restarts. ## Deploy Pomerium Core Start your Pomerium deployment: ```bash docker compose up -d ``` ## Test your setup 1. Navigate to `https://verify.pomerium.yourdomain.com` (using your actual domain). 2. You'll be prompted to sign in with the Pomerium [hosted authenticate service](https://www.pomerium.com/docs/capabilities/authentication.md#hosted-authenticate-service). 3. After authentication, you should see the Pomerium Verify page displaying your identity details. ## Session persistence If you stop and restart the Pomerium container at this point, the next time you access the Pomerium route you may be prompted to sign in again. To avoid this, you'll need to configure Pomerium with a fixed [shared secret](https://www.pomerium.com/docs/reference/shared-secret.md) and a [persistent storage backend](https://www.pomerium.com/docs/reference/databroker.md). To generate a new shared secret you can run this command: ``` head -c32 /dev/urandom | base64 ``` Then add these lines to your config.yaml file: ```yaml title="config.yaml" {1-4} shared_secret: REPLACE_WITH_GENERATED_SECRET databroker_storage_type: file databroker_storage_connection_string: file:///data/pomerium # Pomerium will automatically obtain certificates from Let's Encrypt autocert: true # Routes routes: - from: https://verify.pomerium.yourdomain.com to: http://verify:8000 pass_identity_headers: true policy: - allow: or: - email: is: user@example.com ``` ## Next Steps - [Learn Core Fundamentals](https://www.pomerium.com/docs/get-started/fundamentals/core/get-started.md) - [Configure Advanced Policies](https://www.pomerium.com/docs/get-started/fundamentals/core/build-policies.md) - [Set up a Custom Identity Provider](https://www.pomerium.com/docs/get-started/fundamentals/core/self-hosted-pomerium.md) --- ### Additional Getting Started - [Build Advanced Policies](https://www.pomerium.com/docs/get-started/fundamentals/core/advanced-policies.md): In lesson 5, you'll learn how to build advanced policies. - [Build Advanced Routes](https://www.pomerium.com/docs/get-started/fundamentals/core/advanced-routes.md): In this lesson, you'll learn how to build advanced routes. - [Build a Simple Policy](https://www.pomerium.com/docs/get-started/fundamentals/core/build-policies.md): In lesson 3, you'll learn how to build authorization policies and apply them to your routes. - [Build Simple Routes](https://www.pomerium.com/docs/get-started/fundamentals/core/build-routes.md): In lesson 2, you'll learn how to define routes in your Pomerium configuration file. - [Putting It All Together](https://www.pomerium.com/docs/get-started/fundamentals/core/conclusion.md): This tutorial summarizes all that you've learned with suggestions on what to do next. - [Get Started With Pomerium](https://www.pomerium.com/docs/get-started/fundamentals/core/get-started.md): In lesson 1 of Pomerium Fundamentals, you'll configure and run a Pomerium instance with Docker. - [Identity Verification with JWTs](https://www.pomerium.com/docs/get-started/fundamentals/core/jwt-verification.md): In lesson 4, you'll learn how to set up Pomerium to verify a user's identity with JSON Web Tokens (JWTs). - [Production Certificates with Autocert](https://www.pomerium.com/docs/get-started/fundamentals/core/production-certificates.md): Learn how to add production certificates to your Pomerium instance with Autocert. - [Self-Hosted Authenticate Service](https://www.pomerium.com/docs/get-started/fundamentals/core/self-hosted-pomerium.md): In this tutorial, you'll learn how to self-host the Pomerium Authenticate service. - [Build TCP Routes](https://www.pomerium.com/docs/get-started/fundamentals/core/tcp-routes.md): In this lesson, you'll secure TCP connections to SSH, Postgres, and Redis services with Pomerium. - [Advanced Policies](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-advanced-policies.md): Build advanced authorization policies in Pomerium Zero using chained policy blocks, operators, criteria, and matchers. - [Advanced Routes](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-advanced-routes.md): Configure advanced route settings in Pomerium Zero including headers, path matching, path rewriting, and more. - [Build Policies](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-build-policies.md): Learn how policies work in Pomerium Zero. You'll build a simple authorization policy that protects access to Grafana. - [Build Routes](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-build-routes.md): In this guide, learn how to configure a route in Pomerium Zero that secures an instance of Grafana. - [Custom Identity Provider](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-custom-idp.md): In this guide, learn how to configure a custom identity provider in the Zero Console. - [Single Sign On](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-single-sign-on.md): Set up single sign-on in Pomerium Zero by forwarding JWTs as identity headers to upstream services like Grafana. - [TCP Routes](https://www.pomerium.com/docs/get-started/fundamentals/zero/zero-tcp-routes.md): Proxy TCP and SSH connections through Pomerium Zero using Pomerium CLI to secure non-HTTP services. ## Core Concepts ### Authentication and Single Sign-On (SSO) Source: https://www.pomerium.com/docs/capabilities/authentication.md # Authentication and Single Sign-On (SSO) Pomerium provides authentication and single sign-on (SSO) by authenticating against your identity provider (IdP). While Pomerium itself is not an IdP, it supports conventional IdP solutions and can be configured to work with any IdP that uses the [OpenID Connect](https://openid.net/connect/) (OIDC) protocol. See [identity providers](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md) for step-by-step guides on how to integrate supported IdPs with Pomerium. ## Authentication with Pomerium Pomerium speaks to your IdP to verify user identities and authenticate users to upstream applications. After Pomerium has verified and authenticated the user, the Authentication service creates a local session with relevant session data, including OAuth and ID tokens, which Pomerium uses to authenticate and authorize users and manage Pomerium sessions. ### JWT verification and upstream applications After Pomerium's Authentication service obtains OAuth and ID tokens and OIDC claims from your IdP, it stores that session data in the Databroker service, never leaking it to the client or the upstream application. This provides an extra layer of security, as it prevents the application from using OAuth tokens provided by the IdP. Pomerium mints a new Pomerium JWT based on the claims and scopes in the OAuth and ID tokens and signs the JWT with a private key so the upstream application can verify the incoming request came from Pomerium. Although [JWT verification](https://www.pomerium.com/docs/capabilities/getting-users-identity.md) is optional, Pomerium provides a way for you to [verify a user's identity](https://www.pomerium.com/docs/capabilities/getting-users-identity.md) on the application level by forwarding [JWT claim headers](https://www.pomerium.com/docs/reference/jwt-claim-headers.md) with proxied requests to upstream applications. Pomerium offers frontend and backend SDKs to simplify JWT verification for application developers: - [JavaScript SDK](https://github.com/pomerium/js-sdk) - [Node and Express SDK](https://github.com/pomerium/js-sdk/tree/main/examples/express) - [Go SDK](https://github.com/pomerium/sdk-go) ## SSO support for legacy applications Legacy apps that may not directly support SSO are still compatible with Pomerium. As a reverse proxy, Pomerium is designed to sit in front of your applications. By configuring your applications to route requests to Pomerium's Proxy service, Pomerium can manage the authentication flow and secure your legacy app with minimal to no work on your end. ## Direct IdP Token Authentication Pomerium also supports authenticating users using an Identity Provider’s access token directly, without a full OAuth browser redirect flow. If a user or service has already obtained a valid access token or identity token, they can present it to Pomerium to gain access, rather than going through the usual login redirect. Pomerium will validate the token with the IdP and create a session for the associated user. ### Benefits **Streamlined CLI and Service Auth:** This feature is great for command-line tools and automated workflows. For example, a developer who has run `az login` (Azure CLI) and has an access token can use that token to authenticate with Pomerium via an `Authorization: Bearer ` header or through the Pomerium CLI. This avoids needing a browser during CLI use, making automation smoother. **Use Existing Tokens:** In scenarios where another system has already obtained a user token (for instance, an external authenticator or a device that can’t easily do interactive logins), Pomerium can now accept that token directly. This opens up integration possibilities where Pomerium acts as a resource server in an OAuth flow. **Secure Validation:** Pomerium verifies the token’s signature and claims with the IdP (ensuring it’s not expired, issued for the correct client, etc.) before trusting it. Only tokens from configured, trusted IdPs are accepted. ### Limitations Direct IdP token authentication is an opt-in capability that can be enabled through [Pomerium configuration](https://www.pomerium.com/docs/reference/bearer-token-format.md). This enhancement makes Pomerium even more flexible in hybrid environments where not all clients are web browsers. ## External data sources (Enterprise) [Enterprise customers](https://www.pomerium.com/enterprise-sales/) can enforce context-aware access with Pomerium's [external data sources](https://www.pomerium.com/docs/capabilities/integrations.md) feature (directory sync). From the Enterprise Console, you can import external data from sources other than your IdP. User identity context such as users, groups, roles, language, time zones, location, and more can be included into your authorization policy so you can make granular access control decisions. ## Hosted Authenticate Service Pomerium's **Hosted Authenticate Service** provides an IdP that you can use without registering a client ID and client secret. It can be used with open-source Pomerium, Pomerium Zero, and Pomerium Enterprise. ### How the hosted authenticate service works When using the hosted authenticate service, users will see a Pomerium-branded login page with the option to sign in using email/password credentials, a Google account, or a GitHub account. ### Why use the hosted authenticate service The hosted authenticate service offers a quicker way for users to deploy and test Pomerium. You don't need to register a client directly with a third-part IdP or manage client credentials. If you're testing Pomerium for the first time, run [Pomerium with Docker](https://www.pomerium.com/docs/get-started/fundamentals/core/get-started.md) using our hosted authenticate service – you can run Pomerium Core in **under 5 minutes** with minimal setup. Pomerium users who are interested in [Pomerium Enterprise](https://www.pomerium.com/enterprise-sales/) can test out the [Docker Enterprise quickstart](https://www.pomerium.com/docs/deploy/enterprise/quickstart.md) using our hosted authenticate service as well. ### Configure the hosted authenticate service The hosted authenticate service is enabled and configured by default. Just add a route and policy to your configuration file: ```yaml title="pomerium-config.yaml" routes: - from: https://verify.localhost.pomerium.io to: http://verify:8000 policy: - allow: or: - email: is: user@example.com pass_identity_headers: true ``` ### Compatibility with Native SSH access In order to use the hosted authenticate service in combination with [native SSH](https://www.pomerium.com/docs/capabilities/native-ssh-access.md) routes, you will need two additional configuration file settings. You will need to choose an [authenticate service URL](#authenticate-service-url) and you will need to explicitly specify `idp_provider: hosted`. For example: ```yaml title="pomerium-config.yaml" authenticate_service_url: https://authenticate.localhost.pomerium.io idp_provider: hosted ``` ### Limitations **Limited sign-in options** Users can authenticate with email and password credentials, a Google account, or a GitHub account. **Session management** The hosted authenticate service is separate from your Pomerium installation. This means you can't refresh session tokens, so users must re-authenticate after roughly one hour. **Uptime commitment** We make no commitments to uptime for our free hosted authenticate service. ### Privacy considerations Users that take advantage of our hosted authenticate service should review the [Terms of Service agreement](https://www.pomerium.com/pomerium-zero-user-agreement/). Specifically, you should be aware that by using our hosted services, you agree to Pomerium collecting the following data: - IP address - OS version - Internal domain name - Session details (email, name, and domain) We collect this information to better understand how our users interact with and use Pomerium's services. ## Custom IdP You can use Pomerium with any OIDC-compatible [identity provider](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md). You will need to configure Pomerium with: - an authenticate service URL - client credentials from your IdP ### Authenticate service URL The authenticate service URL is a public-facing URL that should resolve to your Pomerium instance. (If running in [split service mode](https://www.pomerium.com/docs/internals/configuration.md#all-in-one-vs-split-service-mode), this URL should resolve specifically to the authenticate service.) Whenever a user is required to log in to access a route, Pomerium will redirect the user to the authenticate service using this URL. This URL should not contain a path or query parameters. For example, `https://authenticate.corp.example.com`. This URL must be different from any of the route URLs you use with Pomerium. See the [Authenticate Service URL](https://www.pomerium.com/docs/reference/service-urls.md#authenticate-service-url) reference page for details on how to configure this setting. In a typical OIDC authentication flow, all redirect URLs must be pre-registered with the IdP. Pomerium uses a single authenticate service URL so that you need to register only one redirect URL with your IdP. This way, Pomerium can protect multiple upstream services without requiring you to update your IdP client whenever you add a new service. ### Identity provider client Pomerium relies on an IdP to authenticate users. Pomerium can use any IdP that implements the OpenID Connect (OIDC) protocol. The steps to configure your IdP will vary depending on the specific provider. See [**Identity Providers**](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md) for a list of guides to configure commonly-used IdPs. You will generally need to configure your IdP client to allow an OAuth redirect URL based on your chosen authenticate service URL, with the path `/oauth2/callback` appended to it. For example, if your authenticate service URL is `https://authenticate.corp.example.com`, you should configure your identity provider to allow the redirect URI `https://authenticate.corp.example.com/oauth2/callback`. Once you have set up an IdP client, you will need to configure Pomerium with the IdP name, client ID and client secret, and possibly a URL assigned to your individual IdP client. See the [Identity Provider Settings](https://www.pomerium.com/docs/reference/identity-provider-settings.md#identity-provider-client-id) reference page for details on how to configure these settings. ## Single sign-out (SSO) signs a user out of a secured application and clears both the remote application session generated by the identity provider and the local Pomerium session. ## OIDC Front-Channel Logout Pomerium supports Front-Channel Logout as described in [OpenID Connect Front-Channel Logout 1.0](https://openid.net/specs/openid-connect-frontchannel-1_0.html). ### Identity provider support See if your identity provider (IdP) supports Front-Channel Logout by checking your IdP's `/.well-known/openid-configuration` endpoint. If your IdP supports it, you may see something similar to the JSON data below: ```json { "frontchannel_logout_session_supported": true } ``` ### Configure single sign-out To correctly implement SSO, you must register a `frontchannel_logout_uri` for your application that includes the path `/.pomerium/sign_out`. The `frontchannel_logout_uri` must match the external route in your policy. For example, `app.corp.example.com/.pomerium/sign_out`. If configured correctly, the following actions occur: 1. The proxy service receives the sign-out request and clears the local Pomerium session 2. The proxy service redirects the request to the authenticate service URL's `/.pomerium/sign_out` endpoint 3. The authentication service clears the remote application session \[Single sign-out flow] While it is possible to implement SSO using the **authenticate service URL** and the `/.pomerium/sign_out` endpoint, this method is not recommended. Signing out with the authenticate service URL won't delete the session cookie on the route itself. To terminate the remote application and Pomerium sessions, you must point sign-out requests to the external route. ### Single sign-out example with Pomerium The example below demonstrates how to correctly implement SSO using the [JavaScript SDK](https://www.pomerium.com/docs/capabilities/getting-users-identity.md): ```js export const signOut = (redirectUrl) => { let location = window.location.origin + '/.pomerium/sign_out'; if (redirectUrl) { location += '?pomerium_redirect_uri=' + encodeURIComponent(redirectUrl); } window.location.href = location; }; ``` In the example above, the `signOut` function appends the `/.pomerium/sign_out` endpoint to the external route URL, then redirects the user. From here, the proxy service clears the external route session and redirects the user to the authenticate service URL to clear the Pomerium session. --- ### Authorization and Policy Enforcement with Pomerium Source: https://www.pomerium.com/docs/capabilities/authorization.md Pomerium enforces dynamic, context-aware authorization on every request. This capability extends across deployments of any size or complexity, from single-route use cases to multi-namespace or multi-cluster enterprise environments. Below, we cover how to write and apply policies with **Pomerium Policy Language (PPL)**, when to use **Rego**, and how **Namespaces** (Enterprise) and **Clusters** (Pomerium Zero) fit into the picture. ## Introduction: Authorization at Every Layer Pomerium's approach to authorization is continuous and context-aware, integrating identity information from your IdP, device identity, or external data sources. - **Route-based** control in all editions - **Namespace-based** and **cluster-based** organization in Enterprise and Zero - **Policy languages**: [PPL](#pomerium-policy-language-ppl) for most use cases, [Rego](#rego-policies) for advanced logic ## Where Policies Live 1. **Routes**
Policies can be attached to each route, controlling who and what can access the upstream service. 2. **Namespaces**
A namespace is an organizational unit. Policy can be applied once and inherited by child namespaces or routes. Admins can delegate control so teams manage their own routes without harming global security. 3. **Clusters**
Zero-managed clusters pull their config (routes, policies, certificates) from a hosted control plane. Each cluster has its own environment. You can define policies in the Zero console, and they're synced to local Pomerium Core replicas. ## Policy Configuration Approaches ### Pomerium Policy Language (PPL) [Pomerium Policy Language](https://www.pomerium.com/docs/internals/ppl.md) is YAML-based and covers the majority of use cases. - **Actions**: `allow` or `deny` - **Logical Operators**: `and`, `or`, `not`, `nor` - **Criteria**: email, domain, groups, day of week, device, etc. A minimal example: ```yaml allow: and: - domain: is: example.com deny: or: - email: is: spammer@example.com - email: is: malicious@example.com ``` **Deny** overrides **allow**. Requests must pass at least one `allow` rule and no `deny` rules. #### PPL in Pomerium Enterprise Enterprise adds a visual **Policy Builder** and extended criteria (like time-of-day or external data records). You can build policy via GUI or raw PPL: \[Build an authorization policy with PPL in the Enterprise Console] Reapply policies across multiple routes or namespaces: \[Reapply policies to multiple routes or namespaces] ### Rego Policies **Rego** is available to Enterprise customers who need advanced, custom logic beyond what PPL offers. [Rego](https://www.openpolicyagent.org/docs/latest/#rego) is the language used by [Open Policy Agent (OPA)](https://www.openpolicyagent.org/). In Pomerium, you can write Rego modules that produce `allow` or `deny` outcomes. For example: ```rego allow := true ``` or: ```rego deny := [true, "unauthorized"] ``` You can inspect request data under `input.http` (method, headers, path), or session details under `input.http.session`. Learn more in the [Rego docs](https://www.pomerium.com/docs/internals/ppl.md#rego). \[Apply Rego in Console editor] ## Enterprise Features ### Namespaces Namespaces group resources and teams in a hierarchical structure. A parent namespace can enforce global rules while child namespaces add local restrictions. Enterprise also supports [**Clusters**](https://www.pomerium.com/docs/internals/clusters-for-enterprise.md). Key benefits: - **Self-Service**: Team leads can manage their own routes and policies. - **Hierarchical**: Global admins set top-level constraints (like requiring a `@yourcompany.com` email). - **RBAC**: Access is granted via roles: **Guest**, **Viewer**, **Manager**, **Admin**. ## Pomerium Zero & Clusters **Pomerium Zero** uses a *cluster* model. Each cluster is a local deployment of Pomerium Core, connected to a hosted control plane. Clusters fetch routes, policies, and certificates from the Zero console: - **Starter domain**: Each cluster gets a unique domain with automatic TLS. - **Custom domains**: Switch from the starter domain to your own. - **Scalability**: Add more replicas to handle more traffic. - **Storage**: Use a persistent file or PostgreSQL database in production for reliability. For more details, see the [**Clusters**](https://www.pomerium.com/docs/internals/clusters.md) documentation. ## Policy Overrides Regardless of PPL or Rego, Pomerium provides quick overrides: - **Any Authenticated User**: Bypasses all other policy logic and admits any logged-in user. - **CORS Preflight**: Lets `OPTIONS` requests pass unauthenticated. - **Public Access**: No authentication required. Use with caution. If a route is fully public, `robots.txt` will be proxied from upstream (instead of returning a disallow-by-default). ## Putting It All Together 1. **Plan Your Structure** - Small setups: attach a PPL policy directly to each route in Core. - Larger orgs: use **Namespaces** (Enterprise) to group resources. - Multi-deployment: use **Clusters** (Pomerium Zero) to unify config. 2. **Decide on Language** - **PPL**: YAML, easy to read, covers most use cases. - **Rego** (Enterprise): advanced logic, OPA-based. 3. **Continuously Verify** - Pomerium reevaluates the user's context (IdP claims, device posture, location) on every request. ## Learn More - [**Routing**](https://www.pomerium.com/docs/capabilities/routing.md) - [**Policy Language (PPL)**](https://www.pomerium.com/docs/internals/ppl.md) - [**Rego**](https://www.pomerium.com/docs/internals/ppl.md) - [**Namespaces**](https://www.pomerium.com/docs/internals/namespacing.md) - [**Clusters**](https://www.pomerium.com/docs/internals/clusters.md) - [**Device Identity**](https://www.pomerium.com/docs/integrations/device-context/device-identity.md) Use Pomerium's robust, context-driven authorization to unify your security posture, whether you're looking to secure a few routes or an entire global infrastructure. --- ### Routing, Proxying, and Load Balancing with Pomerium Source: https://www.pomerium.com/docs/capabilities/routing.md ## Routing A **Route** defines how to access a service running behind Pomerium. This includes authentication (both for Pomerium and passed through to the service), rewrites, header management, load balancing, etc. When first installing Pomerium Enterprise, users may want to import existing routes from the open-source Pomerium core. The **Migrate Routes** button accepts the open-source `config.yaml` file and imports routes from it to Pomerium Enterprise. \[Migrate routes in Console] From the main Routes page you can view and manage existing routes. From the table of routes you can: - Filter visible routes - Delete one or more routes - Move routes between Namespaces - Export one or more route definitions to a CSV file - Create a JSON-formatted policy report on one or more selected routes The sections below cover the options available when creating or editing a route. ### General The **General** tab defines the route path, both from the internet and to the internal service, and the policies attached. Note that policies enforced on a [Namespace](https://www.pomerium.com/docs/internals/namespacing.md#hierarchical-policy-enforcement) the route resides in will also be applied. #### Name This value is only visible in the Console UI. #### From `From` is the externally accessible URL for the proxied request. Specifying `tcp+https` or `udp+https` for the scheme enables [TCP proxying](https://www.pomerium.com/docs/capabilities/non-http.md) or [UDP proxying](https://www.pomerium.com/docs/capabilities/non-http/udp.md) support for the route. You may map more than one port through the same hostname by specifying a different `:port` in the URL. Only secure schemes (`https`, `tcp+https` and `udp+https`) are supported. #### Metrics Name Once a Route is created, the Metric Name field will populate. You can use this name to scrape the Prometheus service for metrics on this Route when making custom dashboards. #### To `To` is the destination(s) of a proxied request. It can be an internal resource, or an external resource. Multiple upstream resources can be targeted by using a list instead of a single URL: ```yaml - from: https://example.com to: - https://a.example.com - https://b.example.com ``` Both `http://` and `https://` URLs are supported, as well as `http+unix:///var/run/example.sock` and `https+unix:///var/run/example.sock` for local unix addresses. If the `from` address starts with `tcp+https://`, use `tcp://` for the upstream. If the `from` address starts with `udp+https://`, use `udp://` for the upstream. A load balancing weight may be associated with a particular upstream by appending `,[weight]` to the URL. The exact behavior depends on your [`lb_policy`](#load-balancing-method) setting. With rule: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com/anything ``` Requests to `https://verify.corp.example.com` will be forwarded to `https://verify.pomerium.com/anything`, while requests to `https://verify.corp.example.com/foo` will be forwarded to `https://verify.pomerium.com/anythingfoo`.\ To make the request forwarded to `https://httbin.org/anything/foo`, you can use double slashes in your request `https://httbin.corp.example.com//foo`. While the rule: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com/anything/ ``` All requests to `https://verify.corp.example.com/*` will be forwarded to `https://verify.pomerium.com/anything/*`. That means accessing `https://verify.corp.example.com` will be forwarded to `https://verify.pomerium.com/anything/`. That said, if your application does not handle trailing slash, the request will end up with 404 not found. Either `redirect` or `to` must be set. #### Redirect `Redirect` is used to redirect incoming requests to a new URL. The `redirect` field is an object with several possible options: - `https_redirect` (boolean): the incoming scheme will be swapped with "https". - `scheme_redirect` (string): the incoming scheme will be swapped with the given value. - `host_redirect` (string): the incoming host will be swapped with the given value. - `port_redirect` (integer): the incoming port will be swapped with the given value. - `path_redirect` (string): the incoming path portion of the URL will be swapped with the given value. - `prefix_rewrite` (string): the incoming matched prefix will be swapped with the given value. - `response_code` (integer): the response code to use for the redirect. Defaults to 301. - `strip_query` (boolean): indicates that during redirection, the query portion of the URL will be removed. Defaults to false. Either `redirect` or `to` must be set. #### Pass Identity Headers When enabled, this option will pass identity headers to upstream applications. These headers include: - X-Pomerium-Jwt-Assertion - X-Pomerium-Claim-\* #### Policies Add or remove Policies to be applied to the Route. Note that Policies enforced in the Route's Namespace will be applied automatically. For information and examples related to PPL, see [**Policy Language**](https://www.pomerium.com/docs/internals/ppl.md). ::: #### Enable Google Cloud Serverless Authentication Enable sending a signed [Authorization Header](https://cloud.google.com/run/docs/authenticating/service-to-service) to upstream GCP services. Requires setting [Google Cloud Serverless Authentication Service Account](https://www.pomerium.com/docs/reference/google-cloud-serverless-authentication-service-account.md) or running Pomerium in an environment with a GCP service account present in default locations. ### Matchers #### Path If set, the route will only match incoming requests with a path that is an exact match for the specified path. #### Regex If set, the route will only match incoming requests with a path that matches the specified regular expression. The supported syntax is the same as the Go [regexp package](https://golang.org/pkg/regexp/) which is based on [re2](https://github.com/google/re2/wiki/Syntax). #### Regex Rewrite Pattern The pattern to match before rewriting, ex: `^/service/([^/]+)(/.*)$`. #### Regex Rewrite Substitution The substitution for your regex pattern, ex: `\\2/instance/\\1`. #### Prefix If set, the route will only match incoming requests with a path that begins with the specified prefix. #### Prefix Rewrite If set, indicates that during forwarding, the matched prefix (or path) should be swapped with this value. For example, given this policy: ```yaml from: https://from.example.com to: https://to.example.com prefix: /admin prefix_rewrite: / ``` A request to `https://from.example.com/admin` would be forwarded to `https://to.example.com/`. ### Timeouts #### Allow Websockets If set, enables proxying of websocket connections. **Use with caution:** websockets are long-lived connections, so [global timeouts](https://www.pomerium.com/docs/reference/global-timeouts.md) are not enforced (though the policy-specific `timeout` is enforced). Allowing websocket connections to the proxy could result in abuse via [DOS attacks](https://www.cloudflare.com/learning/ddos/ddos-attack-tools/slowloris/). #### Allow SPDY If set, enables proxying of SPDY protocol upgrades. #### Timeout Policy timeout establishes the per-route timeout value. Cannot exceed global timeout values. #### Idle Timeout If you are proxying long-lived requests that employ streaming calls such as websockets or gRPC, set this to either a maximum value there may be no data exchange over a connection (recommended), or set it to unlimited (`0s`). If `idle_timeout` is specified, and `timeout` is not explicitly set, then `timeout` would be unlimited (`0s`). You still may specify maximum lifetime of the connection using `timeout` value (i.e. to 1 day). ### Headers #### Host Headers The `host` header can be preserved via the `preserve_host_header` setting or customized via three mutually exclusive options: 1. `preserve_host_header` will, when enabled, this option will pass the host header from the incoming request to the proxied host, instead of the destination hostname. It's an optional parameter of type `bool` that defaults to `false`. See [ProxyPreserveHost](http://httpd.apache.org/docs/2.0/mod/mod_proxy.html#proxypreservehost). 2. `host_rewrite`, which will rewrite the host to a new literal value. 3. `host_rewrite_header`, which will rewrite the host to match an incoming header value. 4. `host_path_regex_rewrite_pattern` & `host_path_regex_rewrite_substitution`, which will rewrite the host according to a regex matching the path. For example with the following config: ```yaml host_path_regex_rewrite_pattern: '^/(.+)/.+$' host_path_regex_rewrite_substitution: \1 ``` Would rewrite the host header to `example.com` given the path `/example.com/some/path`. The 2nd, 3rd and 4th options correspond to the Envoy route action host related options, which can be found [here](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto.html#config-route-v3-routeaction). #### Set Request Headers Set Request Headers allows you to set static values for given request headers. This can be useful if you want to pass along additional information to upstream applications as headers, or set authentication header to the request. For example: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com policy: - allow: or: - email: is: user@example.com set_request_headers: # works auto-magically! # https://verify.corp.example.com/basic-auth/root/hunter42 Authorization: Basic cm9vdDpodW50ZXI0Mg== X-Your-favorite-authenticating-Proxy: 'Pomerium' ``` Neither `:-prefixed` pseudo-headers nor the `Host:` header may be modified via this mechanism. Those headers may instead be modified via mechanisms such as `prefix_rewrite`, `regex_rewrite`, and `host_rewrite`. #### Remove Request Headers Remove Request Headers allows you to remove given request headers. This can be useful if you want to prevent privacy information from being passed to upstream applications. For example: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com policy: - allow: or: - email: is: user@example.com remove_request_headers: - X-Email - X-Username ``` #### Rewrite Response Headers Rewrite Response Headers allows you to modify response headers before they are returned to the client. The `header` field will match the HTTP header name, and `prefix` will be replaced with `value`. For example, if the upstream server returns a header: ```text Location: http://localhost:8000/two/some/path/ ``` And the policy has this config: ```yaml rewrite_response_headers: - header: Location prefix: http://localhost:8000/two/ value: http://frontend/one/ ``` The browser would be redirected to: `http://frontend/one/some/path/`. This is similar to nginx's [`proxy_redirect` option](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect), but can be used for any header. ### Load Balancer #### Multiple Upstream Servers You may specify multiple servers for your upstream application, and Pomerium would load balance user requests between them. ```yaml routes: - from: https://myapp.localhost.pomerium.io to: - http://myapp-srv-1:8080 - http://myapp-srv-2:8080 ``` In the presence of multiple upstream servers, make sure to specify either an **active** or **passive** health check, or both, to avoid requests being served to an unhealthy backend. #### Active Health Checks Active health checks issue periodic requests to each upstream to determine its health. See [Envoy Health Checking](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/health_checking) for more details. #### Passive Health Checks Passive health check tries to deduce upstream server health based on recent observed responses. See [Outlier Detection](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/outlier) for a comprehensive overview. ```yaml routes: - from: https://myapp.localhost.pomerium.io to: - http://myapp-srv-1:8080 - http://myapp-srv-2:8080 outlier_detection: {} ``` #### Load Balancing Method `lb_policy` should be set to [one of the values](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers): - [`ROUND_ROBIN`](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers#weighted-round-robin) (default) - [`LEAST_REQUEST`](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers#weighted-least-request)\ *May be further configured via [`least_request_lb_config`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#envoy-v3-api-msg-config-cluster-v3-cluster-leastrequestlbconfig).* - [`RING_HASH`](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers#ring-hash)\ *May be further configured via [`ring_hash_lb_config`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster-ringhashlbconfig).* - [`RANDOM`](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers#random) - [`MAGLEV`](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/load_balancing/load_balancers#maglev)\ *May be further configured via [`maglev_lb_config`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#envoy-v3-api-msg-config-cluster-v3-cluster-maglevlbconfig).* > **Some policy types support additional advanced configuration**, such as [least request randomization](https://www.pomerium.com/docs/reference/routes/load-balancing-policy-config.md). See [Load Balancing Policy Config](https://www.pomerium.com/docs/reference/routes/load-balancing-policy-config.md) for details. ##### Example ```yaml routes: - from: https://myapp.localhost.pomerium.io to: - http://myapp-srv-1:8080 - http://myapp-srv-2:8080 - http://myapp-srv-3:8080 - http://myapp-srv-4:8080 - http://myapp-srv-5:8080 lb_policy: LEAST_REQUEST least_request_lb_config: choice_count: 2 # current envoy default ``` #### Load Balancing Weight When a list of upstream URLs is specified in the `to` field, you may append an optional load balancing weight parameter. The individual [`lb_policy`](#load-balancing-method) settings will take this weighting into account when making routing decisions. ##### Example ```yaml routes: - from: https://myapp.localhost.pomerium.io to: - http://myapp-srv-1:8080,10 - http://myapp-srv-2:8080,20 - http://myapp-srv-3:8080,30 - http://myapp-srv-4:8080,20 - http://myapp-srv-5:8080,10 ``` ### Route Matching Order Pomerium matches routes in a specific order; an incoming request will be sent to the first route that matches. Pomerium considers routes defined in a configuration file first, in the order in which they appear in the configuration file, followed by any routes defined in the Enterprise Console or by the Ingress Controller. Routes from the Enterprise Console or Ingress Controller are sorted in the following order: 1. Ascending by `host` 2. Descending by `path` 3. Descending by `regex` 4. Descending by `prefix` The example below defines two routes in a configuration file that both point to the same domain. The first route contains a prefix (`api`). If Pomerium receives a request to access `https://a.example.com:8080`, Pomerium will first check `https://a.example.com:8080/api` before matching the request with `https://a.example.com:8080`. ```yaml title="config.yaml" - from: https://a.example.com:8080 to: http://example:8080 prefix: /api allow_any_authenticated_user: true - from: https://a.example.com:8080 to: http://example:8080 allow_any_authenticated_user: true ``` ### Advanced #### Allow Upgrades If set, enable proxying of an HTTP upgrade request and its payload. This option takes a list of strings and can be set both at the global level for all routes and for a specific route (in which case it overrides the global option). ```yaml allow_upgrades: - custom-upgrade-type - websocket - spdy/3.1 ``` Using `websocket` is equivalent to enabling the `allow_websockets` option and `spdy/3.1` is equivalent to enabling the `allow_spdy` option. ## Certificates Certificates are the x509 *public-key* and *private-key* used to establish secure HTTP and gRPC connections. Any combination of the above can be used together, and are additive. You can also use any of these settings in conjunction with `Autocert` to get OCSP stapling. Certificates loaded into Pomerium from these config values are used to attempt secure connections between end users and services, between Pomerium services, and to upstream endpoints. For example, if specifying multiple certificates at once: ```yaml certificates: - cert: '$HOME/.acme.sh/authenticate.example.com_ecc/fullchain.cer' key: '$HOME/.acme.sh/authenticate.example.com_ecc/authenticate.example.com.key' - cert: '$HOME/.acme.sh/verify.example.com_ecc/fullchain.cer' key: '$HOME/.acme.sh/verify.example.com_ecc/verify.example.com.key' - cert: '$HOME/.acme.sh/prometheus.example.com_ecc/fullchain.cer' key: '$HOME/.acme.sh/prometheus.example.com_ecc/prometheus.example.com.key' ``` Or to set a single certificate and key covering multiple domains and/or a wildcard subdomain: ```yaml certificate_file: '$HOME/.acme.sh/*.example.com/fullchain.crt' certificate_key: '$HOME/.acme.sh/*.example.com/*.example.com.key' ``` **Note:** Pomerium will check your system's trust/key store for valid certificates first. If your certificate solution imports into the system store, you don't need to also specify them with these configuration keys. --- ### Additional Core Concepts - [Pomerium Routes Portal](https://www.pomerium.com/docs/capabilities/routes-portal.md): How to quickly view the routes you have access to in Pomerium. ## Deployment ### Pomerium Core (Self-managed) Source: https://www.pomerium.com/docs/deploy/core.md # Pomerium Core (Self-managed) Pomerium Core (often referred to as *Pomerium Open Source*) is the primary server component in a self-hosted environment. All other Pomerium products build upon it. This document describes several ways to install and run Pomerium Core: 1. **Pre-Built Binaries** (manual or OS-package installations) 2. **Docker Images** 3. **Building from Source** ## Pre-Built Binaries We publish official binaries for Linux and macOS on our [GitHub Releases](https://github.com/pomerium/pomerium/releases) page, as well as OS packages (`deb` and `rpm`) via [Cloudsmith](https://cloudsmith.io/~pomerium/repos/pomerium/packages/). ### Standalone Binary 1. **Download**\ Go to [GitHub Releases](https://github.com/pomerium/pomerium/releases) and look for the tarball corresponding to your operating system and architecture. For example: ```bash ARCH=[amd64 or arm64] OS=[linux or darwin] VERSION=[desired version] curl -L https://github.com/pomerium/pomerium/releases/download/${VERSION}/pomerium-${OS}-${ARCH}.tar.gz \ | tar -z -x ``` 2. **Run**\ Once extracted, you have a `pomerium` binary. Supply configuration via environment variables or a config file: ```bash ./pomerium -config config.yaml ``` ### Linux Packages We provide OS packages via [Cloudsmith](https://cloudsmith.io/~pomerium/repos/pomerium/setup/). Supported formats: - `rpm` (Yum, DNF) - `deb` (Apt) To setup the repository run: ```bash curl -1sLf \ 'https://dl.cloudsmith.io/public/pomerium/pomerium/setup.deb.sh' \ | sudo -E bash ``` Then install Pomerium via your package manager: ```bash # For yum-based systems: yum install pomerium # For apt-based systems: apt-get update && apt-get install pomerium ``` ## Docker Images We also provide container images on [Docker Hub](https://hub.docker.com/r/pomerium/pomerium). Common tags: - **`:latest`** → The most recent stable release - **`:vX.Y.Z`** → A specific release - **`:main`** → Nightly builds from the main branch - **`:nonroot-*`** → Variants that run Pomerium as a `nonroot` user - **`:debug-*`** → Variants that include extra debugging utilities Example usage: ```bash # config.yaml is your Pomerium configuration. # See https://www.pomerium.com/docs/deploy/core#configuration # # Note: The external port (8443) can be changed without affecting your route configuration # as long as your routes don't specify explicit ports. See # https://pomerium.com/docs/reference/routes/from#port-matching-behavior for more information docker pull pomerium/pomerium:latest docker run --rm -it \ -p 8443:443 \ -v $(pwd)/config.yaml:/pomerium/config.yaml \ pomerium/pomerium:latest ``` If you plan to run on port 443 in a rootless environment, you may need extra [capabilities](https://linux-audit.com/linux-capabilities-hardening-linux-binaries-by-removing-setuid/) or choose a non-privileged port. ## Building From Source (~~Hard~~ Fun mode!) If you prefer building from source: 1. **Clone the Repository** ```bash git clone https://github.com/pomerium/pomerium.git $HOME/pomerium cd $HOME/pomerium ``` 2. **(Optional) Generate Local Certs**\ For local development, use [mkcert](https://mkcert.dev/): ```bash go install filippo.io/mkcert@latest mkcert -install mkcert '*.localhost.pomerium.io' ``` 3. **Build** ```bash make ``` This compiles the `pomerium` binary under `./bin`. If you don't have test prerequisites installed (Docker, Redis, etc.), run `make build` to skip them. 4. **Run** ```bash ./bin/pomerium -config config.yaml ``` ## Configuration Pomerium is configured via [configuration variables](https://www.pomerium.com/docs/reference.md) (environment variables) or a YAML file (`config.yaml`). Below is a minimal example referencing a single route and an identity provider: ```yaml title="config.yaml" # Minimal example route # # Generate a shared secret by running head -c32 /dev/urandom | base64 # More on shared secrets at https://www.pomerium.com/docs/reference/shared-secret shared_secret: REPLACE_ME # Generate a cookie secret by running head -c32 /dev/urandom | base64 # More on cookie secrets at https://www.pomerium.com/docs/reference/cookies#cookie-secret cookie_secret: REPLACE_ME # If the Authenticate Service URL is not set, the Pomerium Hosted Authenticate Service will be used. # See https://www.pomerium.com/docs/reference/service-urls#authenticate-service-url # # authenticate_service_url: REPLACE_ME # # For more information on identity provider settings, see https://pomerium.com/docs/reference/identity-provider-settings # # idp_provider: REPLACE_ME # idp_client_id: REPLACE_ME # idp_client_secret: REPLACE_ME address: :443 routes: - from: https://verify.localhost.pomerium.io to: https://verify.pomerium.com policy: - allow: or: - domain: is: myorg.com ``` For local testing, specify the `certificate_file` and `certificate_key_file` if using mkcert or other local certs. In production, you may rely on Let's Encrypt or external cert manager. See [TLS certificates](https://www.pomerium.com/docs/internals/certificates-and-tls.md) for details. ## Running Pomerium ### Systemd Service (OS Packages) If you installed via `rpm` or `deb`, we ship a systemd service unit: 1. **Bind to Port 443**\ Allow the `pomerium` service to listen on a privileged port: ```bash echo -e "[Service]\nAmbientCapabilities=CAP_NET_BIND_SERVICE" | sudo SYSTEMD_EDITOR=tee systemctl edit pomerium ``` 2. **Enable & Start** ```bash sudo systemctl enable --now pomerium.service ``` ### Manual Launch If using the standalone binary (or building from source): ```bash ./pomerium -config config.yaml ``` Any environment variables or custom settings can be set before this command. Once deployed and configured, you can verify that Pomerium is running by accessing the domain of one of your routes. If your logs show successful user authentication, you're ready to protect more apps with Pomerium Core. --- ### Additional Deployment - [Run Pomerium Enterprise With Docker](https://www.pomerium.com/docs/deploy/enterprise/quickstart.md): Demo Pomerium Enterprise - [Kubernetes Quickstart](https://www.pomerium.com/docs/deploy/k8s/quickstart.md): Deploy Pomerium Core to a Kubernetes cluster using the Pomerium Ingress Controller and hosted authenticate service. - [Billing in Pomerium Zero](https://www.pomerium.com/docs/deploy/cloud/billing.md): Learn how to manage subscriptions and billing in Pomerium Zero. - [Pomerium Clients for Tunneling Non-HTTP Protocols](https://www.pomerium.com/docs/deploy/clients.md): Consolidated guide to installing Pomerium CLI/Desktop and configuring TCP+UDP routes in Pomerium. - [Configuration](https://www.pomerium.com/docs/deploy/enterprise/configure.md) - [Global Configuration](https://www.pomerium.com/docs/deploy/k8s/configure.md): This article describes global configuration options. - [Configure Metrics in Pomerium Enterprise](https://www.pomerium.com/docs/deploy/enterprise/configure-metrics.md): Learn how to configure Prometheus to power metrics in the Pomerium Enterprise Console. - [Pomerium Enterprise](https://www.pomerium.com/docs/deploy/enterprise.md): Learn what features come with Pomerium Enterprise, including a Console GUI where you can manage your policies, namespaces, groups, routes, and more. - [Gateway API](https://www.pomerium.com/docs/deploy/k8s/gateway-api.md) - [Import Core Config to Pomerium Zero](https://www.pomerium.com/docs/deploy/cloud/import.md): Learn how to import an existing Pomerium Core configuration into a Pomerium Zero cluster. - [Pomerium Ingress Controller for Kubernetes](https://www.pomerium.com/docs/deploy/k8s/ingress.md): Configure routes, policies, and TLS settings using the Pomerium Ingress Controller for Kubernetes. - [Install](https://www.pomerium.com/docs/deploy/cloud/install.md): This page includes the Pomerium Zero installation steps for supported deployment environments. - [Install](https://www.pomerium.com/docs/deploy/enterprise/install.md): Install Pomerium Enterprise Console alongside Pomerium Core using Docker, Kubernetes, or system packages. - [Installation](https://www.pomerium.com/docs/deploy/k8s/install.md): This article describes installation and customization of the Ingress Controller deployment. - [Kubernetes Deployment Reference](https://www.pomerium.com/docs/deploy/k8s/reference.md): Reference for Pomerium settings in Kubernetes deployments. - [Sync to Pomerium Zero / Enterprise](https://www.pomerium.com/docs/deploy/k8s/sync-api.md): Use the Pomerium Ingress Controller in combination with Pomerium Zero or Pomerium Enterprise. - [Terraform](https://www.pomerium.com/docs/deploy/terraform.md): Learn how to manage your Pomerium configuration using Terraform, including authentication setup, resource management, and deployment examples. - [Upgrading](https://www.pomerium.com/docs/deploy/upgrading.md): Upgrade notes & changelog for both Pomerium Core and Enterprise ## Configuration and Reference ### Allow Any Authenticated User Source: https://www.pomerium.com/docs/reference/routes/allow-any-authenticated-user.md # Allow Any Authenticated User ## Summary **Use with caution**: **Allow Any Authenticated User** allows all requests for any user or service account that authenticates against your identity provider. For example, if you use a corporate GSuite account, an unrelated user with a Gmail account can access the upstream application. Use of this setting means Pomerium **will not enforce your centralized authorization policy** for this route. The upstream is responsible for handling any authorization. ## How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Default** | **Usage** | | :----------------------------- | :-------- | :---------- | :----------- | | `allow_any_authenticated_user` | `boolean` | `false` | **optional** | ### Examples ```yaml allow_any_authenticated_user: true ``` **Enterprise:** Enable **Any Authenticated User** in the **Policy Builder** in the Console: \[Enable any authenticated user in the console] **Kubernetes:** | [**Annotation name**](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations) | **Type** | **Default** | **Usage** | | :-- | :-- | :-- | :-- | | `allow_any_authenticated_user` | `boolean` | `false` | **optional** | ### Examples ```yaml ingress.pomerium.io/allow_any_authenticated_user: 'true' ``` --- ### From Source: https://www.pomerium.com/docs/reference/routes/from.md # From ## Summary The **From** URL is the externally accessible URL for a proxied HTTP request. Specifying `tcp+https` for the scheme enables [TCP proxying](https://www.pomerium.com/docs/capabilities/non-http.md) support for the route; `udp+https` enables [UDP proxying](https://www.pomerium.com/docs/capabilities/non-http/udp.md). (added in v0.29) You may map more than one port through the same hostname by specifying a different `:port` in the URL. Specifying `ssh` for the scheme enables [native SSH proxying](https://www.pomerium.com/docs/capabilities/native-ssh-access.md). ## How to Configure The from URL must contain a **scheme** and **hostname**. It can't contain a path. When defining a From URL, you must use `https`, `tcp+https`, `udp+https`, or `ssh`. Pomerium only supports secure schemes. ### Port Matching Behavior In v0.26, we've updated the route matching behavior. If you do not specify a port number in a route's From URL, the route will match incoming requests with any port number (or no port number). If you do specify a port number, that route will match only requests containing that specific port number. With a from URL like the one below: ```yaml from: https://www.example.com ``` Pomerium will match incoming requests on any port: ```yaml https://www.example.com https://www.example.com:443 https://www.example.com:8443 https://www.example.com:3000 ``` If you specify the port in the From URL, Pomerium will only match incoming requests that specify this port: ```yaml from: https://www.example.com:8443 ``` Previously Pomerium would require an exact match between the incoming request and the From URL port number. You can revert to the previous behavior by setting the [`match_any_incoming_port`](https://www.pomerium.com/docs/reference/runtime-flags.md) runtime flag to false. **Core:** | **YAML**/**JSON** setting | **Type** | **Schemes** | **Usage** | | :-- | :-- | :-- | :-- | | `from` | `URL` | `https`, `tcp+https`, `udp+https`, `ssh` | **required** | ### Examples ```yaml routes: - from: https://verify.corp.example.com to: https://example.com # Native SSH Access - from: ssh://ssh.corp.example.com:22 to: ssh://ssh.local:22 # TCP tunneling - from: tcp+https://ssh.corp.example.com:22 to: tcp://ssh.local:22 # UDP - from: udp+https://time.corp.example.com:13 to: udp://time.local:13 ``` **Enterprise:** **Kubernetes:** See Kubernetes [Ingress](https://www.pomerium.com/docs/deploy/k8s/ingress.md) for more information. See [**Routing - Route matching order**](https://www.pomerium.com/docs/capabilities/routing.md#route-matching-order) for more information on how Pomerium processes and matches routes. ## Wildcard From Routes Kubernetes: Wildcard From Routes in Kubernetes are unofficially supported because Pomerium's implementation behaves differently than what Kubernetes defines in their documentation. See [Wildcard Hostnames](https://kubernetes.io/docs/concepts/services-networking/ingress/#hostname-wildcards) for more information. **Wildcard From Routes** supports the use of a wildcard asterisk (`*`) placed anywhere within the domain name portion of a `from` URL. Defining a `from` route with `*` will point any matching routes to the defined [To route](https://www.pomerium.com/docs/reference/routes/to.md). This eliminates the need to define multiple near-identical routes in your configuration. ([Autocert](https://www.pomerium.com/docs/reference/autocert.md) will be disabled for hosts that use Wildcard From Routes.) For example: ```yaml # Before: routes: - from: https://a.example.com to: https://example.com - from: https://b.example.com to: https://example.com - from: https://c.example.com to: https://example.com - from: https://d.example.com to: https://example.com - from: https://e.example.com to: https://example.com # After routes: - from: https://*.example.com to: https://example.com # Or routes: - from: tcp+https://*.example.com:22 to: tcp://example.com:22 ``` ### Wildcard Processing Behavior Pomerium processes routes in the order they are defined in the configuration file. However, routes which **don't** contain wildcards (`*`) may take precedence over routes which **do** contain wildcards. For example, given the routes below, if you send a request to `foo.example.com`, Pomerium would redirect the request to `1.example.com`. If you send a request to `bar.example.com` (a non-wildcard route), Pomerium would redirect the request to `2.example.com`. ```yaml routes: - from: https://*.example.com to: http://1.example.com - from: https://bar.example.com to: http://2.example.com ``` --- ### Global Timeouts Source: https://www.pomerium.com/docs/reference/global-timeouts.md # Global Timeouts **Global Timeouts** set the global server timeouts for HTTP request and response streams. You can set also set [route-level timeouts](https://www.pomerium.com/docs/reference/routes/timeouts.md). ## Timeout Flow Overview The following diagram shows how different timeouts apply throughout the request lifecycle: ```mermaid sequenceDiagram participant Client participant Pomerium participant Backend (Upstream) Client->>Pomerium: HTTP Request (from client) Note over Client,Pomerium: Read Timeout starts when request begins Pomerium->>Backend (Upstream): Forward request to upstream
(once request is fully received) Note over Pomerium,Backend (Upstream): Default Upstream Timeout starts
after full request is received Backend (Upstream)-->>Pomerium: Upstream Response Pomerium-->>Client: HTTP Response (to client) Note over Pomerium,Client: Write Timeout covers
entire request+response stream Note right of Pomerium: Idle Timeout closes idle connections
with no active requests ``` ## Timeout Settings Overview | **Timeout** | **Direction** | **Default** | **Purpose** | | --- | --- | --- | --- | | **Read Timeout** | Downstream | 30s | Time allowed to receive complete request from client | | **Write Timeout** | Downstream | 0 (disabled) | Total time for entire request/response cycle | | **Idle Timeout** | Bidirectional | 5m | Closes inactive connections when idle | | **Default Upstream Timeout** | Upstream | 30s | Time allowed for backend to respond | ## Read Timeout **Read Timeout** sets the maximum duration allowed for the entire downstream HTTP request to be received. This timeout starts when the downstream client initiates the request and ends when Pomerium has fully received the complete request (including headers and body) from the client. Setting this timeout to `0` disables it. (This is not recommended, as it could allow malicious clients to consume resources with slow or incomplete requests.) ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Default** | | :-- | :-- | :-- | :-- | | `timeout_read` | `TIMEOUT_READ` | [Go Duration] string | `30s` | ### Examples ```yaml timeout_read: 30s ``` ```bash TIMEOUT_READ=30s ``` **Enterprise:** **Kubernetes:** | **[Parameter name](https://www.pomerium.com/docs/deploy/k8s/reference.md#timeouts)** | **Type** | **Defaults** | | :-- | :-- | :-- | | `timeouts.read` | [Go Duration] string | `30s` | ### Examples ```yaml timeouts: read: 30s ``` ## Write Timeout **Write Timeout** sets the maximum duration allowed for an entire HTTP request/response cycle to complete. This includes both the time to receive the request from the downstream client and the time to send the complete response back. This timeout should be greater than the [**Read Timeout**](#read-timeout) setting as it encompasses the entire request/response lifecycle. Setting this timeout to `0` disables it, meaning there is no time limit for request/response completion. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Default** | | :-- | :-- | :-- | :-- | | `timeout_write` | `TIMEOUT_WRITE` | [Go Duration] string | `0` (no timeout) | ### Examples ```yaml timeout_write: 0 ``` ```bash TIMEOUT_WRITE=0 ``` **Enterprise:** **Kubernetes:** | **[Parameter name](https://www.pomerium.com/docs/deploy/k8s/reference.md#timeouts)** | **Type** | **Default** | | :-- | :-- | :-- | | `timeouts.write` | [Go Duration] string | `0` (no timeout) | ### Examples ```yaml timeouts: write: 0 ``` ## Idle Timeout **Idle Timeout** sets the duration after which an upstream or downstream connection will be terminated if there are no active requests or responses. Setting this timeout to `0` disables it, allowing connections to remain open indefinitely when idle. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Default** | | :-- | :-- | :-- | :-- | | `timeout_idle` | `TIMEOUT_IDLE` | [Go Duration] string | `5m` | ### Examples ```yaml timeout_idle: 5m ``` ```bash TIMEOUT_IDLE=5m ``` **Enterprise:** **Kubernetes:** | **[Parameter name](https://www.pomerium.com/docs/deploy/k8s/reference.md#timeouts)** | **Type** | **Default** | | :-- | :-- | :-- | | `timeouts.idle` | [Go Duration] string | `5m` | ### Examples ```yaml timeouts: idle: 5m ``` ## Default Upstream Timeout **Default Upstream Timeout** is the default timeout applied to a proxied route when no `timeout` key is specified by the policy. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Default** | | :-- | :-- | :-- | :-- | | `default_upstream_timeout` | `DEFAULT_UPSTREAM_TIMEOUT` | [Go Duration] string | `30s` | ### Examples ```yaml default_upstream_timeout: 30s ``` ```bash DEFAULT_UPSTREAM_TIMEOUT=1h45m ``` **Enterprise:** Set **Default Upstream Timeout** in the Console: \[Enterprise Console with default timeout setting] **Kubernetes:** Kubernetes does not support `default_upstream_timeout` [go duration]: https://golang.org/pkg/time/#Duration.String --- ### Headers Settings Source: https://www.pomerium.com/docs/reference/routes/headers.md # Headers Settings This reference covers all of Pomerium's **Headers Settings**: - [Host Header Settings](#host-rewrite) - [Set Request Headers](#set-request-headers) - [Remove Request Headers](#remove-request-headers) - [Set Response Headers](#set-response-headers) - [Rewrite Response Headers](#rewrite-response-headers) ## Host Header Settings By default, Pomerium will set the `Host` header of an upstream request according to the host specified in the [**To**](https://www.pomerium.com/docs/reference/routes/to.md) URL for the route. (Strictly speaking, the `Host` header is used only with HTTP/1.1. For HTTP/2 and HTTP/3 the `:authority` pseudo-header is used instead. These settings affect both identically.) You can change how Pomerium sets this header using the following settings: ### 1. Preserve Host Header `preserve_host_header` passes the Host header from the incoming request to the upstream service, instead of the host from the route's To URL. This is like the Apache httpd [ProxyPreserveHost](http://httpd.apache.org/docs/2.0/mod/mod_proxy.html#proxypreservehost) directive. ### 2. Host Rewrite `host_rewrite` rewrites the Host header to a fixed value. ### 3. Host Rewrite Header `host_rewrite_header` rewrites the Host header using the value of some other header from the incoming request. For example: ```yaml host_rewrite_header: X-My-Host ``` For an incoming request with the header `X-My-Host: foo.example.com`, this would rewrite the Host header to `foo.example.com`. ### 4. Host Path Regex Rewrite Pattern/Substitution `host_path_regex_rewrite_pattern` and `host_path_regex_rewrite_substitution` rewrite the Host header based on a regular expression substitution using the URL path as the input. For example: ```yaml host_path_regex_rewrite_pattern: '^/(.+)/.+$' host_path_regex_rewrite_substitution: \1 ``` For a request URL with the path `/example.com/some/path`, this would rewrite the Host header to `example.com`. ### How to Configure **Core:** | **YAML**/**JSON** settings | **Type** | **Usage** | | :------------------------------------- | :-------- | :----------- | | `preserve_host_header` | `boolean` | **optional** | | `host_rewrite` | `string` | **optional** | | `host_rewrite_header` | `string` | **optional** | | `host_path_regex_rewrite_pattern` | `string` | **optional** | | `host_path_regex_rewrite_substitution` | `string` | **optional** | ### Examples ```yaml preserve_host_header: true ``` ```yaml host_rewrite: 'example.com' ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `host_rewrite` | `string` | **optional** | ### Examples ```yaml ingress.pomerium.io/host_rewrite: 'example.com' ``` ### Original Host Header When host rewrite is active, Pomerium adds an `x-pomerium-original-host` header to upstream requests containing the pre-rewrite `Host`/`:authority` value. This applies to the default `auto_host_rewrite` behavior as well as explicit `host_rewrite`, `host_rewrite_header`, and `host_path_regex_rewrite` settings. This header is not added when [`preserve_host_header`](#1-preserve-host-header) is enabled, because the `Host` header is not being rewritten. The header value matches the client's original `Host` exactly, including port if present (e.g. `app.example.com:8443`). Pomerium overwrites any client-sent `x-pomerium-original-host` value, so upstream services can trust it. To remove this header, add it to [`remove_request_headers`](#remove-request-headers): ```yaml remove_request_headers: - x-pomerium-original-host ``` ## Set Request Headers **Set Request Headers** allows you to set both static and dynamic values for given request headers. Static values can be useful if you want to pass along additional information to upstream applications as headers, or to set a fixed authentication header on the request. The dynamic values enable you to pass ID and Access tokens from your identity provider to upstream applications. To pass dynamic values from the user's OIDC claim to an upstream service, see [JWT Claim Headers](https://www.pomerium.com/docs/reference/jwt-claim-headers.md). Neither HTTP/2 [pseudo-headers](https://www.rfc-editor.org/rfc/rfc9113.html#PseudoHeaderFields) (for example, `:authority`) nor the `Host` header may be modified via this mechanism. The request path may instead be modified using the [Path Rewriting Settings](https://www.pomerium.com/docs/reference/routes/path-rewriting.md) and the `Host` header may be modified using the [Host Header Settings](#host-rewrite) above. ### How to Configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :--------------------- | :----------- | | `set_request_headers` | map of key-value pairs | **optional** | ### Examples Pass static header values in the request: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com policy: - allow: or: - email: is: user@example.com set_request_headers: # Set a fixed Basic Auth username and password (root:hunter42) Authorization: Basic cm9vdDpodW50ZXI0Mg== # Set a custom header X-Your-favorite-authenticating-Proxy: 'Pomerium' # To include a '$' character in a header value: X-Hello: $$world # header value is set to "$world" ``` Pass ID token, access token, and client certificate fingerprint (if present) as dynamic headers in the request: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com policy: - allow: or: - email: is: user@example.com set_request_headers: x-pomerium-idp-id-token: ${pomerium.id_token} x-pomerium-idp-access-token: ${pomerium.access_token} x-pomerium-client-cert-fingerprint: ${pomerium.client_cert_fingerprint} ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `set_request_headers` | map of key-value pairs | **optional** | ```yaml ingress.pomerium.io/set_request_headers: | X-Test: X-Value ``` ### Pass Dynamic Tokens in Headers The following token substitutions are available: | **Token** | **Value** | | :-- | :-- | | `${pomerium.access_token}` | OAuth access token from the identity provider\* | | `${pomerium.client_cert_fingerprint}` | Short form SHA-256 fingerprint of the presented client certificate (if [downstream mTLS](https://www.pomerium.com/docs/internals/certificates-and-tls.md) is enabled) | | `${pomerium.client_cert_san_dns}` | Comma-separated list of any DNS Subject Alternative Names (SANs) from the presented client certificate | | `${pomerium.client_cert_san_email}` | Comma-separated list of any email address Subject Alternative Names (SANs) from the presented client certificate | | `${pomerium.id_token}` | OIDC ID token from the identity provider\* | | `${pomerium.jwt}` | [Pomerium JWT](https://www.pomerium.com/docs/capabilities/getting-users-identity.md) (this is the same value as in the [`X-Pomerium-Jwt-Assertion` header](https://www.pomerium.com/docs/reference/routes/pass-identity-headers-per-route.md)) | \*The ID token and access token are not available when using the [Hosted Authenticate](https://www.pomerium.com/docs/capabilities/authentication.md) service. **Note:** Token values must use the `${pomerium.}` syntax. To include a literal `$` character in a header value, use `$$`. Be very careful when passing access tokens to an upstream application. This may allow the application to make other authenticated requests on behalf of the user. ### Rewrite Request Headers In addition to token substitutions, request headers can be rewritten using `${pomerium.request.headers["HEADER-NAME"]}`. For example if a request had a header `X-Jwt: JWT` and you would like to send it as `Authorization: Bearer JWT`, you could do so using: ```yaml set_request_headers: Authorization: Bearer ${pomerium.request.headers["X-Jwt"]} ``` ## Remove Request Headers **Remove Request Headers** allows you to remove given request headers. This can be useful if you want to prevent privacy information from being passed to upstream applications. ### How to Configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :----------------- | :----------- | | `remove_request_headers` | `array of strings` | **optional** | ### Examples ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com policy: - allow: or: - email: is: user@example.com remove_request_headers: - X-Email - X-Username ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `remove_request_headers` | `array of strings` | **optional** | ```yaml ingress.pomerium.io/remove_request_headers: | - X-Email - X-User ``` ## Set Response Headers **Set Response Headers** allows you to set static values for the given response headers. These headers will take precedence over the global [`set_response_headers`](https://www.pomerium.com/docs/reference/set-response-headers.md). ### How to Configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :--------------------- | :----------- | | `set_response_headers` | map of key-value pairs | **optional** | ### Examples ```yaml set_response_headers: X-Test: X-Value ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `set_response_headers` | map of key-value pairs | **optional** | ```yaml ingress.pomerium.io/set_response_headers: | X-Test: X-Value ``` ## Rewrite Response Headers **Rewrite Response Headers** allows you to modify response headers before they are returned to the client. The `header` field will match the HTTP header name, and `prefix` will be replaced with `value`. ### How to Configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------- | :----------------- | :----------- | | `rewrite_response_headers` | `array of objects` | **optional** | ### Examples If the upstream server returns a header: ```text Location: http://localhost:8000/two/some/path/ ``` And the policy has this config: ```yaml rewrite_response_headers: - header: Location prefix: http://localhost:8000/two/ value: http://frontend/one/ ``` The browser would be redirected to: `http://frontend/one/some/path/`. This is similar to nginx's [`proxy_redirect` option](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect), but can be used for any header. **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `rewrite_response_headers` | `array of objects` | **optional** | ### Examples If the upstream server returns a header: ```text Location: http://localhost:8000/two/some/path/ ``` And the Ingress object has this annotation: ```yaml ingress.pomerium.io/rewrite_response_headers: | - header: Location prefix: http://localhost:8000/two/ value: http://frontend/one/ ``` The browser would be redirected to: `http://frontend/one/some/path/`. This is similar to nginx's [`proxy_redirect` option](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect), but can be used for any header. --- ### Identity Provider Settings Source: https://www.pomerium.com/docs/reference/identity-provider-settings.md # Identity Provider Settings This reference covers all of Pomerium's **Identity Provider Settings**: - [Identity Provider Client ID](#identity-provider-client-id) - [Identity Provider Client Secret](#identity-provider-client-secret) - [Identity Provider Client Secret File](#identity-provider-client-secret-file) - [Identity Provider Name](#identity-provider-name) - [Identity Provider Request Params](#identity-provider-request-params) - [Identity Provider Scopes](#identity-provider-scopes) - [Identity Provider URL](#identity-provider-url) - [Identity Provider Polling Min/Max Delay](#identity-provider-polling-minmax-delay) Pomerium uses the [**Hosted Authenticate Service**](https://www.pomerium.com/docs/capabilities/authentication.md) by default. If you want to run Pomerium with a self-hosted authenticate service, include an [**identity provider**](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md) and [**authenticate service URL**](https://www.pomerium.com/docs/reference/service-urls.md#authenticate-service-url) in your configuration. See [**Self-Hosted Authenticate Service**](https://www.pomerium.com/docs/capabilities/authentication.md) for more information. ## Supported identity providers Pomerium supports all major single-sign on (SSO) identity providers. See the [identity providers](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md) page for a list of supported SSO providers and guides to integrate each provider with Pomerium. Pomerium can also integrate with any identity provider that supports OAuth 2.0 and OIDC protocols. ## Identity Provider Client ID **Identity Provider Client ID** is the OAuth 2.0 Client Identifier retrieved from your identity provider. See your identity provider's documentation, and Pomerium's identity provider docs for details. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_client_id` | `IDP_CLIENT_ID` | `string` | **required** (if self-hosting) | #### Examples ```yaml idp_client_id: idp_client_id ``` ```bash IDP_CLIENT_ID=idp_client_id ``` **Enterprise:** **Kubernetes:** See [`identityProvider.secret`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) ## Identity Provider Client Secret **Identity Provider Client Secret** is the OAuth 2.0 Secret Identifier retrieved from your identity provider. See your identity provider's documentation, and Pomerium's identity provider docs for details. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_client_secret` | `IDP_CLIENT_SECRET` | `string` | **required** (unless using [idp\_client\_secret\_file](#identity-provider-client-secret-file)) | #### Examples ```yaml idp_client_secret: idp_client_secret ``` ```bash IDP_CLIENT_SECRET=idp_client_secret ``` **Enterprise:** **Kubernetes:** See [`identityProvider.secret`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) for more information ## Identity Provider Client Secret File **Identity Provider Client Secret File** is the OAuth 2.0 Secret Identifier retrieved from your identity provider. See your identity provider's documentation, and Pomerium's identity provider docs for details. The identity provider client secret file points to a file containing the secret. This is useful when deploying in environments that provide secret management like [Docker Swarm](https://docs.docker.com/engine/swarm/secrets/). ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_client_secret_file` | `IDP_CLIENT_SECRET_FILE` | `string` | **required** (unless using [identity\_provider\_client\_secret](#identity-provider-client-secret)) | #### Examples ```yaml idp_client_secret_file: '/run/secrets/POMERIUM_CLIENT_SECRET' ``` ```bash IDP_CLIENT_SECRET_FILE='/run/secrets/POMERIUM_CLIENT_SECRET' ``` **Enterprise:** `idp_client_secret_file` is a bootstrap configuration setting and is not configurable in the Console. **Kubernetes:** See [`identityProvider.secret`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) for more information. ## Identity Provider Name **Identity Provider Name** is the short-hand name of a built-in OpenID Connect (OIDC) identity provider used for authentication. See identity provider for details. The supported values for this setting are: - `apple` - `auth0` - `azure` - `cognito` - `github` - `gitlab` - `google` - `hosted` (see [Hosted Authenticate Service]) - `oidc` - `okta` - `onelogin` - `ping` ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_provider` | `IDP_PROVIDER` | `string` | **required** (if not using the [Hosted Authenticate Service]) | #### Examples ```yaml idp_provider: auth0 ``` ```bash IDP_PROVIDER=github ``` **Enterprise:** **Kubernetes:** See [`identityProvider.provider`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) for more information ## Identity Provider Request Params **Identity Provider Request Params** lists the parameters you want to include as part of a sign-in request using the OAuth 2.0 code flow. Do not set the `client_id`, `response_type`, `redirect_uri`, `scope`, `state`, or `nonce` parameters here. Pomerium sets these itself as part of the OAuth 2.0 code flow, and overriding them will break sign-in. Use this setting only for additional parameters your identity provider supports (for example, `prompt` or `access_type`). ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_request_params` | `IDP_REQUEST_PARAMS` | `string` (map of key-value pairs) | **optional** | #### Examples ```yaml idp_request_params: foo: bar baz: qux ``` ```bash IDP_REQUEST_PARAMS='{"foo":"bar", "baz":"qux"}' ``` **Enterprise:** **Kubernetes:** See Kubernetes [`identityProvider.requestParams` and `identityProvider.requestParamsSecret`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) for more information ### Defaults Pomerium includes some default parameters for specific identity providers. Setting this configuration option will replace these default parameters. To remove the default parameters entirely, set this option to an empty map\* (e.g. `idp_request_params: {}` in the config file). | **Provider** | **Default parameters** | | :-- | :-- | | `apple`\* |
response\_mode: form\_post
| | `azure` |
prompt: select\_account
| | `google` |
prompt: select\_account consent
access\_type: offline
| \*The default parameters for `apple` behave differently; any values set using this configuration option will be merged with the default parameters. For more information, see: - [OIDC Request Parameters](https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters) - [IANA OAuth Parameters](https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml) - [Microsoft Azure Request params](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code) - [Google Authentication URI parameters](https://developers.google.com/identity/protocols/oauth2/openid-connect) ## Identity Provider Scopes **Identity Provider Scopes** correspond to access privilege scopes as defined in [Section 3.3](https://www.rfc-editor.org/rfc/rfc6749#section-3.3) of OAuth 2.0 RFC6749. The scopes associated with Access Tokens determine what resources will be available when they are used to access OAuth 2.0 protected endpoints. If you are using a built-in provider, you probably don't want to set customized scopes. Some providers, like Amazon Cognito, *do not* support the `offline_access` scope. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_scopes` | `IDP_SCOPES` | `string` (list) | **optional** (for built-in identity providers) | #### Examples ```yaml idp_scopes: openid, profile, offline_access, email ``` ```bash IDP_SCOPES=openid, profile, offline_access, email ``` **Enterprise:** **Kubernetes:** See Kubernetes [`identityProvider.scopes`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) for more information ### Defaults | **Defaults** | | :--------------- | | `openid` | | `profile` | | `email` | | `offline_access` | ## Identity Provider URL **Identity Provider URL** is the base path to an identity provider's [OpenID connect discovery document](https://openid.net/specs/openid-connect-discovery-1_0.html). An example Azure URL would be `https://login.microsoftonline.com/common/v2.0` for [their discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration). "Base path" is defined as the section of the URL to the discovery document up to (but not including) `/.well-known/openid-configuration`. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `idp_provider_url` | `IDP_PROVIDER_URL` | `string` | **required** (depending on identity provider) | #### Examples ```yaml idp_provider_url: 'https://awesome-company.auth0.com' ``` ```bash IDP_PROVIDER_URL='https://awesome-company.auth0.com' ``` **Enterprise:** **Kubernetes:** See Kubernetes [`identityProvider.url`](https://www.pomerium.com/docs/deploy/k8s/reference.md#identityprovider) for more information ## Identity Provider Polling Min/Max Delay Identity provider **Polling Minimum Delay** and **Polling Maximum Delay** settings define the minimum and maximum delay times between requests to the identity provider data source. A job starts with the **minimum delay** intervals. If the job fails to complete within the minimum delay period, it will be interrupted and the job will restart. If the job is interrupted due to timeout or an error, it will restart with increasing intervals up to the **maximum delay** period. The `minimum_delay` and `maximum_delay` settings are an [**Enterprise Console**](https://www.pomerium.com/enterprise-sales/) feature, and are not configurable in Pomerium Core. ### How to configure Set the **Identity Provider Max/Min Delay** settings in the Console: \[Set IdP minimum and maximum polling delay settings in the console] ### Defaults While minimum and maximum polling time defaults are set for any Console installation, the required durations will vary depending on your identity provider and the size or your organization's directory. If the job fails before completing, increase the minimum and maximum durations until the job completes. Keep in mind that large directories may take several hours to complete. ### Monitor directory sync To determine the appropriate durations required to sync your directory, check your Console logs and the **Last Error** and **Request Duration** sections of the Console GUI. #### Last error You can check if an error interrupted a job by checking **External Data** > **Last Error**. \[Check for external data sync errors] #### Request duration You can also check **External Data** > **Metrics** to view request durations. \[Monitor request duration] [hosted authenticate service]: https://www.pomerium.com/docs/capabilities/authentication.md#hosted-authenticate-service --- ### JWT Claim Headers Source: https://www.pomerium.com/docs/reference/jwt-claim-headers.md # JWT Claim Headers ## Summary The **JWT Claims Headers** setting allows you to pass specific user session data to upstream applications as HTTP request headers. Claims forwarded with JWT Claims Headers are not signed by the Authorization Service (unlike the ` X-Pomerium-Jwt-Assertion` header). Forwarding a claim with JWT Claims Headers adds the claim to the X-Pomerium-Jwt-Assertion header if the claim is not already included in the assertion header. Both JWT Claims Headers and the signed assertion header are forwarded with the [`pass_identity_headers`](https://www.pomerium.com/docs/reference/routes/pass-identity-headers-per-route.md) setting. ## How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `jwt_claims_headers` | `JWT_CLAIMS_HEADERS` | map of `string` (or comma-separated `string`) | **optional** | ### Examples ```yaml jwt_claims_headers: X-Email: email X-Username: user ``` **Enterprise:** **Kubernetes:** | **[Parameter name](https://www.pomerium.com/docs/deploy/k8s/reference.md#spec)** | **Type** | **Usage** | | :-- | :-- | :-- | | `jwtClaimHeaders` | map of strings | **optional** | ### Examples ```yaml jwtClaimHeaders: X-Email: email X-Username: user ``` ### Format JWT Claims Headers Any claim in Pomerium's session JWT can be placed into a corresponding header and the JWT payload for upstream consumption. Claim information is sourced from your [identity provider](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md) and Pomerium's own session metadata. The header will have the following format: `X-Pomerium-Claim-{Name}`, where `{Name}` is the name of the requested claim. Underscores will replace dashes. For example, `X-Pomerium-Claim-Given-Name`. ### Customize header names The JWT Claims Headers setting allows you to customize claim headers with a nested object: ```yaml jwt_claims_headers: X-Email: email X-Username: user ``` The JSON payload from this example would look similar to the sample data below: ```json "X-Email": [ "user@example.com" ], "X-Username": [ "user" ] ``` Use this option if you previously relied on `x-pomerium-authenticated-user-{email|user-id|groups}`. ## Standard claims Every JWT that Pomerium issues carries a fixed set of claims describing the session. These claims are always present in the signed assertion header (`X-Pomerium-Jwt-Assertion`), whether or not you configure `jwt_claims_headers`. Naming one of them in `jwt_claims_headers` also copies its value into an `X-Pomerium-Claim-*` header. | Claim | Description | | :-- | :-- | | `iss` | The issuer. By default this is the route's hostname. If [`jwt_issuer_format`](https://www.pomerium.com/docs/reference/routes/jwt-issuer-format.md) is set to `uri`, it is `https://HOSTNAME/` instead. | | `aud` | The audience: the route's hostname. | | `jti` | A unique identifier for the token (a UUID). A new value is generated for every token. | | `iat` | The time the token was issued, in seconds since the Unix epoch. | | `exp` | The time the token expires, in seconds since the Unix epoch. Pomerium sets this to five minutes after `iat`. | | `sub` | The subject: the user's ID. This is the same value as `user`. | | `user` | The user's ID, as assigned by the identity provider. | | `email` | The user's email address. | | `groups` | The user's groups, listing both group IDs and group names. When the user has no groups this is an empty list rather than null. | | `sid` | The session ID. | | `name` | The user's name. | A standard claim always takes precedence: an identity provider claim of the same name cannot override it. ## Additional claims from your identity provider Beyond the standard claims, you can forward any claim that your identity provider supplies about the user. When a user logs in, Pomerium collects claims from two places and stores them on the session: - the **ID token** returned from the identity provider's token endpoint, and - the response from the identity provider's [UserInfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo). Any claim found in either is available to `jwt_claims_headers` by name. The OAuth access token is not read for claims; a claim placed only in the access token has no effect. To forward an additional claim, first configure your identity provider to include it in the ID token or return it from the UserInfo endpoint, then name the claim in `jwt_claims_headers`: ```yaml jwt_claims_headers: X-Department: department ``` Pomerium transforms claim values before forwarding them: - **Nested claims are flattened** to dot-separated names. A claim such as `{ "resource_access": { "app": { "roles": [...] } } }` becomes `resource_access.app.roles`; refer to it by that name. - **A multi-valued claim is forwarded as a single comma-separated string.** For example, a claim whose value is `["a", "b"]` is forwarded as `a,b`. - **Only scalar values — strings, numbers, and booleans — are forwarded.** Entries whose value is an object are skipped. To pass static values as request headers to the upstream service, see [Set Request Headers](https://www.pomerium.com/docs/reference/routes/headers.md#set-request-headers). --- ### JWT Groups Filter Source: https://www.pomerium.com/docs/reference/jwt-groups-filter.md This setting is available only in [Pomerium Enterprise](https://www.pomerium.com/docs/deploy/enterprise.md). ## Summary The **JWT Groups Filter** setting allows you to reduce the size of the groups claim in the [Pomerium JWT](https://www.pomerium.com/docs/capabilities/getting-users-identity.md) when used in combination with [directory sync]. This may be useful for organizations with large numbers of directory groups. When directory sync is enabled, Pomerium will include directory groups membership information in a `groups` claim in the Pomerium JWT. By default, all of a user's groups are included in this claim. However, if the average number of groups is very large, this may grow unwieldy and potentially lead to HTTP header size issues with some upstream services. This feature allows you to limit the size of the `groups` claim by specifying a subset of groups that are relevant for your deployment. Only groups belonging to this subset will be included in the `groups` claim in the Pomerium JWT. The groups eligible for inclusion may be specified explicitly, or inferred automatically from the policies that apply to a given route. The setting also applies to the `Impersonate-Group` header, for Kubernetes API server authentication. This setting can also be customized for a particular route, see [**JWT Groups Filter (per route)**](https://www.pomerium.com/docs/reference/routes/jwt-groups-filter.md). ## How to configure The **JWT Groups Filter** setting is available in the Enterprise Console on the "Settings" page, under the "Proxy" tab. \[screenshot of JWT Groups Filter setting] Select the "Filter to groups referenced in policies" option if you want to automatically filter based on any group IDs referenced in any policies associated with a specific route. Or you can enter specific groups using the "Filter to specific groups" input field. If both are specified, a group will be eligible for inclusion in the Pomerium JWT if it is *either* referenced in an associated policy, or present in the list of specific groups. This setting has no effect if [directory sync] is not enabled. ## Logging When this feature is enabled, a new field `removed-groups-count` will be present in the [authorize logs](https://www.pomerium.com/docs/reference/authorize-log-fields.md). This field will indicate the number of groups that were removed by groups filtering for a specific request. To verify that groups filtering is working as expected, you can also set the [log level](https://www.pomerium.com/docs/reference/log-level.md) to "debug." At this level, Pomerium will log an additional entry with the message `JWT group filtering removed groups` along with the IDs of all removed and included groups. [directory sync]: https://www.pomerium.com/docs/integrations/user-standing/directory-sync.md --- ### JWT Groups Filter (per route) Source: https://www.pomerium.com/docs/reference/routes/jwt-groups-filter.md This setting is available only in [Pomerium Enterprise](https://www.pomerium.com/docs/deploy/enterprise.md). ## Summary The **JWT Groups Filter (per route)** setting allows you to reduce the size of the groups claim in the [Pomerium JWT](https://www.pomerium.com/docs/capabilities/getting-users-identity.md). See the global [**JWT Groups Filter**](https://www.pomerium.com/docs/reference/jwt-groups-filter.md) reference page for more information about this feature. When the global setting is enabled, the per-route option will *expand* the set of allowed groups. A group will be eligible for inclusion in the groups claim if is allowed by either the global setting or the per-route setting. When the global setting is not enabled, the per-route option will enable groups filtering for a specific route. ## How to configure The **JWT Groups Filter** setting is available in the Enterprise Console on the route settings page, under the "Headers" tab. \[screenshot of JWT Groups Filter setting] --- ### Pass Identity Headers Source: https://www.pomerium.com/docs/reference/pass-identity-headers.md # Pass Identity Headers ## Summary When set to true, the **Pass Identity Headers** setting sends identity headers to all upstream applications. To change this behavior for a specific route, use the [route-level Pass Identity Headers](https://www.pomerium.com/docs/reference/routes/pass-identity-headers-per-route.md) setting. Identity headers include: - `X-Pomerium-Jwt-Assertion` - `X-Pomerium-Claim-*` (see [JWT Claim Headers](https://www.pomerium.com/docs/reference/jwt-claim-headers.md) for more information) ## How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | :-- | | `pass_identity_headers` | `PASS_IDENTITY_HEADERS` | `boolean` | **optional** | \*`false` | ### Examples ```yaml pass_identity_headers: true routes: - from: https://service.corp.example.io to: http://localhost:3000 ``` **Enterprise:** **Kubernetes:** | **[Parameter name](https://www.pomerium.com/docs/deploy/k8s/reference.md#spec)** | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | | `passIdentityHeaders` | `boolean` | **optional** | \*`false` | ### Examples ```yaml passIdentityHeaders: true ``` See [Kubernetes - Global Configuration](https://www.pomerium.com/docs/deploy/k8s/configure.md) for more information. \* If neither the **global** *nor* **route-level** `pass_identity_headers` settings are defined, both settings will default to `false`. --- ### Pass Identity Headers (per route) Source: https://www.pomerium.com/docs/reference/routes/pass-identity-headers-per-route.md # Pass Identity Headers (per route) ## Summary When set to true, the **Pass Identity Headers (per route)** setting will pass identity headers to the upstream application. The default value for this setting is controlled by the [global-level Pass Identity Headers](https://www.pomerium.com/docs/reference/pass-identity-headers.md) setting. Identity headers include: - `X-Pomerium-Jwt-Assertion` - `X-Pomerium-Claim-*` (see [JWT Claim Headers](https://www.pomerium.com/docs/reference/jwt-claim-headers.md) for more information) ## How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :-------- | :----------- | | `pass_identity_headers` | `boolean` | **optional** | ### Examples ```yaml routes: - from: https://verify.localhost.pomerium.io to: http://verify:8000 pass_identity_headers: true ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | | `pass_identity_headers` | `boolean` | **optional** | Inherits from global setting | ```yaml ingress.pomerium.io/pass_identity_headers: 'true' ``` --- ### Path Rewriting Settings Source: https://www.pomerium.com/docs/reference/routes/path-rewriting.md # Path Rewriting Settings This reference covers all of Pomerium's **Path Rewriting Settings**: - [Prefix Rewrite](#prefix-rewrite) - [Regex Rewrite Pattern](#regex-rewrite) - [Regex Rewrite Substitution](#regex-rewrite) These settings affect only the *requests* sent to an upstream service. Pomerium will not rewrite responses from an upstream service based on these settings. This includes any link URLs or paths to JavaScript or CSS assets. In general, if you want to expose an upstream service under some URL path prefix, you will *also* need to configure the service so it is aware of this path prefix. Otherwise, absolute URLs in the response may be incorrect and the service may not function correctly. ## Prefix Rewrite If set, **Prefix Rewrite** indicates that during forwarding, the matched prefix (or path) should be swapped with this value. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :------- | :----------- | | `prefix_rewrite` | `string` | **optional** | ### Examples ```yaml from: https://from.example.com to: https://to.example.com prefix: /admin prefix_rewrite: / ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `prefix_rewrite` | `string` | **optional** | A request to `https://from.example.com/admin` would be forwarded to `https://to.example.com/`. ## Regex Rewrite If **Regex Rewrite** or **Regex Rewrite Substitution** are set, the URL path will be rewritten according to the pattern and substitution, similar to [`prefix_rewrite`](https://www.pomerium.com/docs/reference/routes/path-rewriting.md#prefix-rewrite). ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :--------------------------- | :------- | :----------- | | `regex_rewrite_pattern` | `string` | **optional** | | `regex_rewrite_substitution` | `string` | **optional** | ### Examples ```yaml regex_rewrite_pattern: '^/service/([^/]+)(/.*)$' regex_rewrite_substitution: "\\2/instance/\\1" ``` **Enterprise:** **Kubernetes:** Kubernetes does not support **Regex Rewrite** --- ### Policy Source: https://www.pomerium.com/docs/reference/routes/policy.md # Policy ## Summary The `policy` block inside a `route` definition defines the authorization policy applied to a route. Policies are defined using Pomerium Policy Language (**PPL**), a yaml-based markup designed to be easier to read and implement compared to current alternatives. See [Pomerium Policy Language](https://www.pomerium.com/docs/internals/ppl.md) for a full explanation of how to write policies in PPL. ## How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :------------------------ | :----------- | | `policy` | `object` (PPL rule block) | **optional** | ### Examples ```yaml routes: - from: https://verify.localhost.pomerium.io to: http://verify:8000 policy: - allow: and: - domain: is: pomerium.com - user: is: user ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-authorization-policy)** | **Type** | **Usage** | | :-- | :-- | :-- | | `policy` | `object` (PPL rule block) | **optional** | ### Examples ```yaml ingress.pomerium.io/policy: | allow: and: - domain: is: pomerium.com - user: is: user ``` --- ### Public Access Source: https://www.pomerium.com/docs/reference/routes/public-access.md # Public Access ## Summary Because the **Public Access** setting bypasses authentication and authorization checks, you should enable it only for publicly exposed web services. The **Public Access** setting instructs Pomerium to grant unauthorized and unauthenticated access to all requests to the upstream service. If you enable this setting, no other policy should be provided for the route. By default, Pomerium serves a **robots.txt** response directly, instructing search engines *not* to crawl the route domain: ```txt User-agent: * Disallow: / ``` For routes with policies that allow public, unauthenticated access, Pomerium *will not* serve **robots.txt** directly. Instead, Pomerium will proxy requests for `/robots.txt` to the upstream service. ## How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Default** | **Usage** | | :-- | :-- | :-- | :-- | | `allow_public_unauthenticated_access` | `boolean` | `false` | **optional** | ### Examples ```yaml allow_public_unauthenticated_access: true ``` **Enterprise:** Enable **Public Access** in the **Policy Builder** in the Console: \[Enable public access in the console] **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Default** | **Usage** | | :-- | :-- | :-- | :-- | | `allow_public_unauthenticated_access` | `boolean`, `string` | `false` | **optional** | ### Examples ```yaml ingress.pomerium.io/allow_public_unauthenticated_access: 'true' ``` --- ### Routes Source: https://www.pomerium.com/docs/reference/routes.md # Routes - Environment Variable: `ROUTES` - Config File Key: `routes` - Type: [base64 encoded](https://en.wikipedia.org/wiki/Base64) `string` or inline policy structure in config file - **Required** - While Pomerium will start without a route configured, it will not authorize or proxy any traffic until a route is defined. If configuring Pomerium for the Enterprise Console, define a route for the Console itself in Pomerium. A route contains specific access and control definitions for a back-end service. Each route is a list item under the `routes` key. Each route defines at minimum a `from` field and one of [`to`](https://www.pomerium.com/docs/reference/routes/to.md), [`redirect`](https://www.pomerium.com/docs/reference/routes/redirect.md), or [`response`](https://www.pomerium.com/docs/reference/routes/direct-response.md), along with a `policy` key defining authorization logic. Policies are defined using [Pomerium Policy Language](https://www.pomerium.com/docs/internals/ppl.md) (**PPL**). Additional options are listed below. --- ### Service URL Settings Source: https://www.pomerium.com/docs/reference/service-urls.md # Service URL Settings Pomerium's service URL settings control how the different [Pomerium services](https://www.pomerium.com/docs/internals/architecture.md#component-level) communicate with each other. The [authenticate service URL](#authenticate-service-url) setting also controls whether Pomerium will run its own authenticate service or use the [Hosted Authenticate Service](https://www.pomerium.com/docs/capabilities/authentication.md). The other service URL settings are needed only for split service deployments. When running in all-in-one mode (which is the recommended mode), these settings are not needed. ## Authenticate Service URL The **Authenticate Service URL** setting defines the externally accessible URL where Pomerium redirects end users (clients) to authenticate against an identity provider. If not set, Pomerium will use the [Hosted Authenticate Service](https://www.pomerium.com/docs/capabilities/authentication.md). If you prefer to use your own [identity provider](https://www.pomerium.com/docs/integrations/user-identity/identity-providers.md), you'll need to set an authenticate service URL, and you will need this URL when configuring your identity provider client's OAuth callback URL. If Pomerium is running in [split-service mode](https://www.pomerium.com/docs/internals/configuration.md#all-in-one-vs-split-service-mode), each Pomerium service requires the authenticate service URL in its configuration. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `authenticate_service_url` | `AUTHENTICATE_SERVICE_URL` | `URL` | **optional** | ### Examples ```yaml authenticate_service_url: https://authenticate.corp.example.com ``` ```bash AUTHENTICATE_SERVICE_URL=https://authenticate.corp.example.com ``` **Enterprise:** **Kubernetes:** | **[Parameter name](https://www.pomerium.com/docs/deploy/k8s/reference.md#spec)** | **Type** | **Usage** | | :-- | :-- | :-- | | `authenticate.url` | `URL` | **optional** | ### Examples ```yaml authenticate: url: https://authenticate.corp.example.com ``` See the [Kubernetes - Global Configuration](https://www.pomerium.com/docs/deploy/k8s/configure.md#authenticate-endpoint) for more information. ## Authenticate Internal Service URL The **Authenticate Internal Service URL** setting is only required for split-service mode deployments where Pomerium can't access the public Authenticate Service URL. If set, the Authenticate Internal Service URL will be used for communication between other Pomerium services and the authenticate service. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `authenticate_internal_service_url` | `AUTHENTICATE_INTERNAL_SERVICE_URL` | `URL` | \***optional** | \* Excluding the `authenticate_internal_service_url` defaults to the [hosted authenticated service](https://www.pomerium.com/docs/capabilities/authentication.md) if `authenticate_service_url` isn't defined. ### Examples ```yaml authenticate_internal_service_url: https://authenticate.internal ``` ```bash AUTHENTICATE_INTERNAL_SERVICE_URL=https://authenticate.internal ``` **Enterprise:** `authenticate_internal_service_url` is a bootstrap configuration setting and is not configurable in the Console. **Kubernetes:** Kubernetes does not support `authenticate_internal_service_url` ## Authorize Service URL The **Authorize Service URL** setting defines the location of Pomerium's internally accessible Authorize Service. Unlike the Authenticate Service, the Authorize Service has no publicly accessible HTTP handlers, so this setting is purely for gRPC communication. If your load balancer does not support gRPC pass-through, you must set this value to an **internally** routable location (`https://pomerium-authorize-service.default.svc.cluster.local`) instead of an **externally** routable one (`https://authorize.corp.example.com`). ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `authorize_service_url` | `AUTHORIZE_SERVICE_URL` | `URL` | **required** (Inferred in all-in-one mode to be localhost) | | `authorize_service_urls` | `AUTHORIZE_SERVICE_URLS` | `URL` | **required** (Inferred in all-in-one mode to be localhost) | ### Examples You can specify multiple URLs as an array with the `authorize_service_urls` key: ```yaml authorize_service_urls: - https://localhost:5443 - https://authorize.corp.example.com ``` ```bash AUTHORIZE_SERVICE_URL=https://pomerium-authorize-service.default.svc.cluster.local ``` **Enterprise:** **Kubernetes:** The `authorize_service_url` is not customizable in all-in-one mode with the CRD ## Authorize Internal Service URL The **Authorize Internal Service URL** setting is only required for split-service mode deployments where Pomerium can't access the public Authorize Service URL. If included, Authorize Internal Service URL will override Authorize Service URL. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Usage** | | :-- | :-- | :-- | :-- | | `authorize_internal_service_url` | `AUTHORIZE_INTERNAL_SERVICE_URL` | `URL` | **required** (Inferred in all-in-one mode to be localhost) | ### Examples ```yaml authorize_internal_service_url: https://pomerium-authorize-service.default.svc.cluster.local ``` ```bash AUTHORIZE_INTERNAL_SERVICE_URL=https://localhost:5443 ``` **Enterprise:** `authorize_internal_service_url` is a bootstrap configuration setting and is not configurable in the Console. **Kubernetes:** Kubernetes does not support `authorize_internal_service_url` ## Databroker Service URL The **Databroker Service URL** settings points to a databroker which is responsible for storing associated authorization context (for example, sessions, users, and user groups). In all-in-one mode, leave these unset; Pomerium uses `http://localhost:5443`. Set them only when the databroker runs on another host/port (split-service or remote). ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Default** | | :-- | :-- | :-- | :-- | | `databroker_service_url` | `DATABROKER_SERVICE_URL` | `URL` | `http://localhost:5443` (In [all-in-one mode](https://www.pomerium.com/docs/internals/configuration.md#all-in-one-vs-split-service-mode)) | | `databroker_service_urls` | `DATABROKER_SERVICE_URLS` | `URL` | `http://localhost:5443` (In [all-in-one mode](https://www.pomerium.com/docs/internals/configuration.md#all-in-one-vs-split-service-mode)) | #### Examples ```yaml databroker_service_urls: - http://databroker.corp.example1.com - https://databroker.corp.example2.com ``` ```bash DATABROKER_SERVICE_URL=https://databroker.corp.example.com ``` **Enterprise:** **Kubernetes:** `databroker_service_url` is not customizable in Kubernetes for all-in-one mode deployments ## Databroker Internal Service URL The **Databroker Internal Service URL** overrides [`databroker_service_url`](https://www.pomerium.com/docs/reference/databroker.md) when determining the TLS certificate for the Databroker service to listen with. ### How to configure **Core:** | **Config file keys** | **Environment variables** | **Type** | **Default** | | :-- | :-- | :-- | :-- | | `databroker_internal_service_url` | `DATABROKER_INTERNAL_SERVICE_URL` | `URL` | `http://localhost:5443` (In all-in-one mode) | #### Examples ```yaml databroker_internal_service_url: http://localhost:5443 ``` ```bash DATABROKER_INTERNAL_SERVICE_URL=http://localhost:5443 ``` **Enterprise:** `databroker_internal_service_url` is a bootstrap configuration setting and is not configurable in the Console. **Kubernetes:** `databroker_internal_service_url` is not customizable in Kubernetes --- ### Timeouts Settings Source: https://www.pomerium.com/docs/reference/routes/timeouts.md # Timeouts Settings This reference covers all of Pomerium's **Timeouts Settings**: - [Allow Websockets](#websocket-connections) - [SPDY](#spdy) - [Route Timeout](#route-timeout) - [Idle Timeout](#idle-timeout) ## Websocket Connections If set, enables proxying of websocket connections. **Use with caution:** websockets are long-lived connections, so both the route timeout and idle timeout are automatically disabled (set to `0`) unless explicitly configured. Allowing websocket connections to the proxy could result in abuse via [DOS attacks](https://www.cloudflare.com/learning/ddos/ddos-attack-tools/slowloris/). ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Default** | | :------------------------ | :-------- | :---------- | | `allow_websockets` | `boolean` | `false` | ### Examples ```yaml allow_websockets: true ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Default** | | :-- | :-- | :-- | | `allow_websockets` | `boolean`, `string` | `false` | ### Examples ```yaml ingress.pomerium.io/allow_websockets: 'true' ``` ## SPDY If set, **SPDY** enables proxying of SPDY protocol upgrades. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Default** | | :------------------------ | :-------- | :---------- | | `allow_spdy` | `boolean` | `false` | ### Examples ```YAML allow_spdy: true ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Default** | | :-- | :-- | :-- | | `allow_spdy` | `boolean` | `false` | ### Examples ```yaml ingress.pomerium.io/allow_spdy: 'true' ``` See [Kubernetes - Ingress Configuration](https://www.pomerium.com/docs/deploy/k8s/ingress.md) for more information. ## Route Timeout **Route Timeout** establishes the per-route timeout value. Cannot exceed [global timeout](https://www.pomerium.com/docs/reference/global-timeouts.md) values. If unset, the route inherits the global [`default_upstream_timeout`](https://www.pomerium.com/docs/reference/global-timeouts.md) (default `30s`). For routes with websockets enabled, or for TCP, UDP, and Kubernetes routes, the route timeout is automatically set to `0` (disabled) unless explicitly configured via this setting. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | | `timeout` | `string` ([Go Duration](https://golang.org/pkg/time/#Duration.String) formatting) | **optional** | `30s` | ### Examples ```yaml timeout: 30s ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | | `timeout` | `string` ([Go Duration](https://golang.org/pkg/time/#Duration.String) formatting) | **optional** | `30s` | ### Examples ```yaml ingress.pomerium.io/timeout: '30s' ``` ## Idle Timeout If you are proxying long-lived requests that employ streaming calls such as websockets or gRPC, set **Idle Timeout** to either a maximum value there may be no data exchange over a connection (recommended), or set it to unlimited (`0s`). If `idle_timeout` is specified, and [route timeout](https://www.pomerium.com/docs/reference/routes/timeouts.md#route-timeout) is not explicitly set, then the route timeout would be unlimited (`0s`). You still may specify maximum lifetime of the connection using the route timeout value (i.e. to 1 day). For routes with websockets enabled, or for TCP, UDP, and Kubernetes routes, the idle timeout is automatically set to `0` (disabled) unless explicitly configured via this setting. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | | `idle_timeout` | `string` ([Go Duration](https://golang.org/pkg/time/#Duration.String) formatting) | **optional** | not set | ### Examples ```yaml routes: - from: https://verify.localhost.pomerium.io to: http://verify:8000 timeout: 1m idle_timeout: 5m ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | **Default** | | :-- | :-- | :-- | :-- | | `idle_timeout` | `string` ([Go Duration](https://golang.org/pkg/time/#Duration.String) formatting) | **optional** | not set | ### Examples ```yaml ingress.pomerium.io/timeout: '1m' ingress.pomerium.io/idle_timeout: '5m' ``` --- ### TLS Settings Source: https://www.pomerium.com/docs/reference/routes/tls.md # TLS Settings This reference covers all of Pomerium's TLS route settings: - [TLS Client Certificate](#tls-client-certificate) - [TLS Custom Certificate Authority](#tls-custom-certificate-authority) - [TLS Downstream Client Certificate Authority](#tls-downstream-client-certificate-authority) - [TLS Downstream Server Name](#tls-downstream-server-name) - [TLS Server Name](#tls-server-name) - [TLS Skip Verification](#tls-skip-verification) - [TLS Upstream Allow Renegotiation](#tls-upstream-allow-renegotiation) - [TLS Upstream Server Name](#tls-upstream-server-name) ## TLS Client Certificate If specified, Pomerium will present this client certificate to upstream services when requested to enforce [mutual authentication](https://en.wikipedia.org/wiki/Mutual_authentication) (mTLS). For more details, see our [mTLS example repository](https://github.com/pomerium/pomerium/tree/main/examples/mutual-tls) and the [Upstream mTLS With Pomerium](https://www.pomerium.com/docs/internals/certificates-and-tls.md) guide. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :-- | :-- | :-- | | `tls_client_cert` and `tls_client_key` | `string` (base64-encoded) | **optional** | | `tls_client_cert_file` and `tls_client_key_file` | `string` (file path) | **optional** | ### Examples ```yaml tls_client_cert: base64-encoded-certificate tls_client_key: base64-encoded-keywords # or tls_client_cert_file: /relative/file/location tls_client_key_file: /relative/file/location ``` **Enterprise:** **Kubernetes:** See Kubernetes [TLS Certificates](https://www.pomerium.com/docs/deploy/k8s/ingress.md#tls-certificates) for more information ## TLS Custom Certificate Authority **TLS Custom Certificate Authority** defines a set of root certificate authorities that the Pomerium Proxy Service uses when verifying upstream server certificates. **Note**: This setting will replace (not append) the system's trust store for a given route. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :-------------------------------------- | :------- | :----------- | | `tls_custom_ca` or `tls_custom_ca_file` | `string` | **optional** | ### Examples ```yaml tls_custom_ca: base64-encoded-custom-ca tls_custom_ca_file: /relative/file/location ``` **Enterprise:** **Kubernetes:** See Kubernetes [TLS Certificates](https://www.pomerium.com/docs/deploy/k8s/ingress.md#tls-certificates) for more information ## TLS Downstream Client Certificate Authority This setting is deprecated, and will be removed in a future release. If you previously used this setting to require client certificates only on certain routes, you can achieve this same behavior by setting the new downstream mTLS [**Enforcement Mode**](https://www.pomerium.com/docs/reference/downstream-mtls-settings.md#enforcement-mode) option to the value `policy` and adding a policy deny rule with the `invalid_client_certificate` criterion on all routes that should require client certificates. If you want to enforce an allowlist or denylist of specific certificates on a particular route, you can use the new [`client_certificate`](https://www.pomerium.com/docs/internals/ppl.md#certificate-matcher) policy criterion. If you do need to set completely different trusted client CAs for different routes, we recommend running separate Pomerium clusters for each set of trusted client CAs. If specified, downstream clients (like a user's browser) will be required to provide a valid client TLS certificate. This overrides the global `downstream_mtls.ca` option for this route. See [Client-Side mTLS With Pomerium](https://www.pomerium.com/docs/internals/mutual-auth.md) for more information. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :-- | :-- | :-- | | `tls_downstream_client_ca` or `tls_downstream_client_ca_file` | `string` | **optional** | ### Examples ```yaml tls_downstream_client_ca: base64-encoded-client-ca tls_downstream_client_ca_file: /relative/file/location ``` **Enterprise:** **Kubernetes:** See Kubernetes [TLS Certificates](https://www.pomerium.com/docs/deploy/k8s/ingress.md#tls-certificates) for more information ## TLS Downstream Server Name **TLS Downstream Server Name** overrides the hostname specified in the `from` field. When a connection to Pomerium is made via TLS the `tls_downstream_server_name` will be used as the expected Server Name Indication, whereas the host part of the `from` field, will be expected to match the `Host` or `:authority` headers of the HTTP request. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :--------------------------- | :------- | :----------- | | `tls_downstream_server_name` | `string` | **optional** | **Enterprise:** **Kubernetes:** Kubernetes does not support `tls_downstream_server_name` ## TLS Server Name **TLS Server Name** overrides the hostname specified in the `to` field for TLS certificate verification and SNI. This is useful when your backend is an HTTPS server with a valid certificate, but you want to communicate with it using an internal hostname (for example, a Docker container name). If [`tls_upstream_server_name`](#tls-upstream-server-name) is also set, it takes precedence over this setting. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------ | :------- | :----------- | | `tls_server_name` | `string` | **optional** | ### Examples ```yaml tls_server_name: backend.internal.example.com ``` **Enterprise:** Configure **TLS Server Name** in the Console under the route TLS settings. **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Usage** | | :-- | :-- | :-- | | `tls_server_name` | `string` | **optional** | ### Examples ```yaml ingress.pomerium.io/tls_server_name: 'backend.internal.example.com' ``` ## TLS Skip Verification TLS Skip Verification controls whether the Pomerium Proxy Service verifies the upstream server's certificate chain and host name. If enabled, Pomerium accepts any certificate presented by the upstream server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Default** | | :------------------------ | :-------- | :---------- | | `tls_skip_verify` | `boolean` | `false` | ### Examples ```yaml tls_skip_verify: true ``` **Enterprise:** **Kubernetes:** | **[Annotation name](https://www.pomerium.com/docs/deploy/k8s/ingress.md#set-ingress-annotations)** | **Type** | **Default** | | :-- | :-- | :-- | | `tls_skip_verify` | `boolean` | `false` | ### Examples ```yaml ingress.pomerium.io/tls_skip_verify: 'true' ``` ## TLS Upstream Allow Renegotiation **TLS Upstream Allow Renegotiation** controls whether server-initiated TLS renegotiation is allowed for upstream servers. For more details, see Envoy's documentation on [`allow_renegotiation`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/tls.proto). TLS renegotiation is considered insecure and shouldn't be used unless absolutely necessary. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Default** | | :--------------------------------- | :-------- | :---------- | | `tls_upstream_allow_renegotiation` | `boolean` | `false` | ### Examples ```yaml tls_upstream_allow_renegotiation: true ``` **Enterprise:** **Kubernetes:** Kubernetes does not support **TLS Upstream Allow Renegotiation** ## TLS Upstream Server Name **TLS Upstream Server Name** overrides the hostname specified in the `to` field for TLS certificate verification and SNI. If set, this server name will be used to verify the certificate name. This is useful when the backend of your service is a TLS server with a valid certificate, but mismatched name. This setting takes precedence over [`tls_server_name`](#tls-server-name) if both are set. ### How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | | :------------------------- | :------- | :----------- | | `tls_upstream_server_name` | `string` | **optional** | **Enterprise:** **Kubernetes:** Kubernetes does not support `tls_upstream_server_name` --- ### To Source: https://www.pomerium.com/docs/reference/routes/to.md # To ## Summary **To** is the destination(s) of a proxied request. It can be an internal resource, or an external resource. ## How to configure **Core:** | **YAML**/**JSON** setting | **Type** | **Usage** | **Schemes** | | :-- | :-- | :-- | :-- | | `to` | `URL` | **optional** | `http`, `http+unix`, `https`, `https+unix`, `h2c`, `tcp`, `udp`, `ssh` | ### Examples ```yaml - from: https://example.com to: http://verify - from: https://example.com to: https://192.1.20.12:8080 - from: https://example.com to: http://neverssl.com - from: https://example.com to: https://verify.pomerium.com/anything/ - from: https://example.com to: https+unix:///var/run/example.sock # Native SSH Access - from: ssh://ssh.corp.example.com:22 to: ssh://internal-ssh-server:22 # TCP tunneling - from: tcp+https://db.corp.example.com:5432 to: tcp://postgres-server:5432 ``` **Enterprise:** **Kubernetes:** See Kubernetes [Ingress](https://www.pomerium.com/docs/deploy/k8s/ingress.md) for more information. ### Target multiple upstream resources Multiple upstream resources can be targeted by using a list instead of a single URL: ```yaml - from: https://example.com to: - https://a.example.com - https://b.example.com ``` ### Set load balancing weight A load balancing weight may be associated with a particular upstream by appending `,[weight]` to the URL. The exact behavior depends on your [`load_balancing_policy`](https://www.pomerium.com/docs/reference/routes/load-balancing.md#load-balancing-policy) setting. See [Load Balancing](https://www.pomerium.com/docs/capabilities/routing.md) for example [configurations](https://www.pomerium.com/docs/capabilities/routing.md#load-balancing-weight). ```yaml - from: https://example.com to: ['http://a', 'http://b'] - from: https://example.com to: ['http://a,10', 'http://b,20'] ``` ### HTTP/2 cleartext When Pomerium connects to an `https` upstream, it will negotiate either HTTP/1.1 or HTTP/2 using [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation) (as part of the TLS handshake). To configure Pomerium to make requests to an upstream service using HTTP/2 *without* TLS (that is, in cleartext), use the special `h2c://` scheme: ```yaml - from: https://example.com to: h2c://localhost:9090 ``` The HTTP/2 specification refers to this case as having *"[prior knowledge](https://datatracker.ietf.org/doc/html/rfc9113#name-starting-http-2-with-prior-)"* that a server supports HTTP/2. One use case is connecting to an insecure gRPC server. As gRPC requires HTTP/2, a client has "prior knowledge" that the server supports HTTP/2. ### TCP Routes You can configure Pomerium to handle a [TCP route](https://www.pomerium.com/docs/capabilities/non-http.md) in one of two different ways. If you specify a `to` URL with the `tcp://` scheme, Pomerium will proxy the raw TCP connection to the upstream service: ```yaml - from: tcp+https://tcp.example.com:3001 to: tcp://localhost:3001 ``` If you specify a `to` URL with the scheme `http://` or `https://`, Pomerium will instead proxy an HTTP CONNECT request to the upstream service: ```yaml - from: tcp+https://tcp.example.com:3001 to: http://second-proxy.tcp.example.com:3002 ``` This allows you to place Pomerium in front of another HTTP-to-TCP proxy. If you specify a list of multiple `to` URLs in one route, you may not include both `tcp://` and non-`tcp://` URLs. ### UDP Routes Starting in v0.29, you can configure Pomerium to handle a [UDP route](https://www.pomerium.com/docs/capabilities/non-http/udp.md) in one of two different ways. If you specify a `to` URL with the `udp://` scheme, Pomerium will proxy the raw UDP connection to the upstream service: ```yaml - from: udp+https://udp.example.com:3001 to: udp://localhost:3001 ``` If you specify a `to` URL with the scheme `http://` or `https://`, Pomerium will instead proxy an HTTP CONNECT-UDP request to the upstream service: ```yaml - from: udp+https://udp.example.com:3001 to: https://second-proxy.udp.example.com:3002 ``` This allows you to place Pomerium in front of another HTTP-to-UDP proxy. If you specify a list of multiple `to` URLs in one route, you may not include both `udp://` and non-`udp://` URLs. See [**Routing - Route matching order**](https://www.pomerium.com/docs/capabilities/routing.md#route-matching-order) for more information on how Pomerium processes and matches routes. Be careful with trailing slash. With rule: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com/anything ``` Requests to `https://verify.corp.example.com` will be forwarded to `https://verify.pomerium.com/anything`, while requests to `https://verify.corp.example.com/foo` will be forwarded to `https://verify.pomerium.com/anythingfoo`.To make the request forwarded to `https://httbin.org/anything/foo`, you can use double slashes in your request `https://httbin.corp.example.com//foo`. While the rule: ```yaml - from: https://verify.corp.example.com to: https://verify.pomerium.com/anything/ ``` All requests to `https://verify.corp.example.com/*` will be forwarded to `https://verify.pomerium.com/anything/*`. That means accessing to `https://verify.corp.example.com` will be forwarded to `https://verify.pomerium.com/anything/`. That said, if your application does not handle trailing slash, the request will end up with 404 not found. Either `to`, [`redirect`](https://www.pomerium.com/docs/reference/routes/redirect.md), or [`response`](https://www.pomerium.com/docs/reference/routes/direct-response.md) must be set. --- ### Additional Configuration and Reference - [Google Cloud Serverless Authentication Service Account](https://www.pomerium.com/docs/reference/google-cloud-serverless-authentication-service-account.md): Manually set Google Cloud Serverless Authentication Service Account credentials with this setting. - [Enable Google Cloud Serverless Authentication](https://www.pomerium.com/docs/reference/routes/enable-google-cloud-serverless-authentication.md): Send signed authorization headers to upstream GCP services like Cloud Run, Cloud Functions, and App Engine. - [Access Log Fields](https://www.pomerium.com/docs/reference/access-log-fields.md): Use Access Log Fields to display HTTP request logs from the proxy service. - [Additional Login Redirect Hosts](https://www.pomerium.com/docs/reference/routes/additional-login-redirect-hosts.md) - [Address](https://www.pomerium.com/docs/reference/address.md): Address specifies the host and port to serve HTTPS requests from. - [Authorize Log Fields](https://www.pomerium.com/docs/reference/authorize-log-fields.md): Use Authorize Log Fields to display HTTP request logs from the authorize service. - [Autocert Settings](https://www.pomerium.com/docs/reference/autocert.md): Autocert lets Pomerium manage public-facing TLS certificates automatically. - [Bearer Token Format](https://www.pomerium.com/docs/reference/bearer-token-format.md): Bearer token format controls how HTTP bearer token authentication is handled. - [Branding Settings](https://www.pomerium.com/docs/reference/branding.md): Customize your Console with Pomerium's branding settings. - [Certificates Settings](https://www.pomerium.com/docs/reference/certificates.md) - [Circuit Breaker Thresholds](https://www.pomerium.com/docs/reference/circuit-breaker-thresholds.md): Circuit Breaker Thresholds customizes circuit-breaking behavior. - [Name](https://www.pomerium.com/docs/reference/routes/cluster-name.md) - [Codec Type](https://www.pomerium.com/docs/reference/codec-type.md) - [Cookies Settings](https://www.pomerium.com/docs/reference/cookies.md): Learn how to configure Pomerium's cookies settings. - [CORS Preflight](https://www.pomerium.com/docs/reference/routes/cors-preflight.md) - [Databroker Storage Settings](https://www.pomerium.com/docs/reference/databroker.md) - [Debug Settings](https://www.pomerium.com/docs/reference/debug.md): Configure debug settings in Pomerium. - [Route Description](https://www.pomerium.com/docs/reference/routes/description.md) - [Direct Response](https://www.pomerium.com/docs/reference/routes/direct-response.md) - [DNS Settings](https://www.pomerium.com/docs/reference/dns.md): DNS Settings - [Downstream mTLS Settings](https://www.pomerium.com/docs/reference/downstream-mtls-settings.md): Downstream mTLS settings configure the client certificate requirements for end users connecting to Pomerium-managed routes. - [Envoy Admin Interface](https://www.pomerium.com/docs/reference/envoy-admin-interface.md): These options can be used to enable and customize the Envoy admin interface. - [gRPC Settings](https://www.pomerium.com/docs/reference/grpc.md): Configure gRPC configuration settings in Pomerium. - [HTTP Redirect Address](https://www.pomerium.com/docs/reference/http-redirect-address.md): If set, the HTTP Redirect Address specifies the host and port to redirect http to https traffic on. - [Identity Provider Client ID (per route)](https://www.pomerium.com/docs/reference/routes/identity-provider-client-id-per-route.md) - [Identity Provider Client Secret (per route)](https://www.pomerium.com/docs/reference/routes/identity-provider-client-secret-per-route.md) - [JWT Identity Providers](https://www.pomerium.com/docs/reference/identity-providers.md): Identity Providers declares the additional identity providers whose JWT bearer tokens Pomerium verifies on routes using the jwt bearer token format. - [Identity Providers (per route)](https://www.pomerium.com/docs/reference/routes/identity-providers-per-route.md) - [IdP Access Token Allowed Audiences](https://www.pomerium.com/docs/reference/idp-access-token-allowed-audiences.md): IdP access token allowed audiences controls how the audience claim of an incoming IdP-issued access token is validated. - [Insecure Server](https://www.pomerium.com/docs/reference/insecure-server.md): Turning on insecure server mode will result in pomerium starting, and operating without any protocol encryption in transit. - [JWT Issuer Format](https://www.pomerium.com/docs/reference/routes/jwt-issuer-format.md) - [Kubernetes Service Account Token](https://www.pomerium.com/docs/reference/routes/kubernetes-service-account-token.md) - [Load Balancing Settings](https://www.pomerium.com/docs/reference/routes/load-balancing.md): Configure Load Balancer settings in Pomerium. - [Load Balancing Policy Config](https://www.pomerium.com/docs/reference/routes/load-balancing-policy-config.md) - [Log Level](https://www.pomerium.com/docs/reference/log-level.md): Log level sets the global logging level for pomerium. - [Route Logo URL](https://www.pomerium.com/docs/reference/routes/logo-url.md) - [Metrics Settings](https://www.pomerium.com/docs/reference/metrics.md): Configure metrics settings in Pomerium. - [Outlier Detection](https://www.pomerium.com/docs/reference/routes/outlier-detection.md) - [Override Certificate Name](https://www.pomerium.com/docs/reference/override-certificate-name.md): Secure service communication can fail if the external certificate does not match the internally routed service hostname/SNI. - [Path Matching Settings](https://www.pomerium.com/docs/reference/routes/path-matching.md): Configure Path Matching settings in Pomerium. - [Programmatic Redirect Domain Whitelist](https://www.pomerium.com/docs/reference/programmatic-redirect-domain-whitelist.md) - [Proxy Log Level](https://www.pomerium.com/docs/reference/proxy-log-level.md): Sets Envoy's logging level for the Pomerium Proxy service. - [Redirect](https://www.pomerium.com/docs/reference/routes/redirect.md) - [Configuration & Settings Reference](https://www.pomerium.com/docs/reference.md) - [Regex Priority Order](https://www.pomerium.com/docs/reference/routes/regex-priority-Order.md): Determines the ordering of routes that use regular expressions. - [Runtime Flags](https://www.pomerium.com/docs/reference/runtime-flags.md): This page lists Runtime Flags available in Pomerium. - [Service Mode](https://www.pomerium.com/docs/reference/service-mode.md): Sets which Pomerium services to run. Accepts a comma-separated list. - [Set Response Headers](https://www.pomerium.com/docs/reference/set-response-headers.md) - [Shared Secret](https://www.pomerium.com/docs/reference/shared-secret.md): This page discusses shared secret settings in Pomerium, which are used to mutually authenticate requests between Pomerium services. - [Show Error Details](https://www.pomerium.com/docs/reference/routes/show-error-details.md) - [Signing Key](https://www.pomerium.com/docs/reference/signing-key.md): This page discusses the signing key settings Pomerium uses to sign the Pomerium JWT that's sent to upstream services to verify a user's identity. - [Signout Redirect URL](https://www.pomerium.com/docs/reference/signout-redirect-url.md) - [Auto TLS](https://www.pomerium.com/docs/reference/tls-derive.md): Auto TLS for Console and Core seamless connectivity. - [Tracing](https://www.pomerium.com/docs/reference/tracing.md) - [Upstream Bind Settings](https://www.pomerium.com/docs/reference/upstream-bind-settings.md): These options control which network address Pomerium will use for upstream connections. - [Use PROXY Protocol](https://www.pomerium.com/docs/reference/use-proxy-protocol.md) - [X-Forwarded-For Settings](https://www.pomerium.com/docs/reference/x-forwarded-for-settings.md): This page documents X-Forwarded-HTTP header settings. ## Advanced Capabilities ### Delegate MCP Access to an LLM Source: https://www.pomerium.com/docs/capabilities/mcp/delegate-mcp-to-llm.md # Delegate MCP Access to an LLM Let AI agents call MCP tools through Pomerium on behalf of a user. There are two patterns: - **Client application** — build a web app that captures the current user's session token and passes it to an LLM API - **Service account** — mint a long-lived credential for headless agents (CI, Claude Code, Codex) that reuses the user's upstream connections Both patterns enforce the same authorization policies and log every tool call. ## Pattern 1: Client application with token delegation Build a web application where the authenticated user's Pomerium token is captured and passed to an LLM API (such as OpenAI). The LLM then uses that token to call MCP servers behind Pomerium on behalf of the user — with full authentication, authorization, and audit logging. **What you get:** - Your app receives an External Token (TE) for the authenticated user via the `mcp: client` route - Your app passes that token to an LLM API, which uses it to call MCP servers on the user's behalf - Pomerium enforces policies on every MCP tool call — the LLM has exactly the same access as the user - Server discovery API to list available MCP servers and their connection status ## Architecture ```mermaid sequenceDiagram actor U as User participant P as Pomerium participant A as Your App Backend participant L as LLM API (OpenAI) participant S as MCP Server U ->> P: GET https://chat-app.your-domain.com P ->> A: Authorization: Bearer (TE) A ->> P: GET /.pomerium/mcp/routes (discover servers) P ->> A: Server list with connection status A ->> L: Chat request + MCP server URLs + Bearer (TE) L ->> P: tools/call Authorization: Bearer (TE) P ->> S: Proxied request (policy enforced) S ->> P: Tool result P ->> L: Response L ->> A: Chat completion with tool results A ->> U: Rendered response ``` The key insight: your app never calls MCP servers directly. It hands the user's token to the LLM, and the LLM makes the MCP calls through Pomerium. Pomerium enforces the same policies it would for any other client. ## Configuration Your deployment needs two types of routes: a **client** route for your chat app and one or more **server** routes for MCP servers. ```yaml routes: # Your chat app (MCP client) - from: https://chat-app.your-domain.com to: http://chat-app:3000 mcp: client: {} policy: {} # MCP servers the LLM can call - from: https://db-mcp.your-domain.com to: http://db-mcp-server:8080/mcp name: Database Server mcp: server: {} policy: allow: and: - domain: is: company.com # MCP server with upstream OAuth (e.g., GitHub) - from: https://github-mcp.your-domain.com to: http://github-mcp-server:8080/mcp name: GitHub mcp: server: upstream_oauth2: client_id: xxxxxxxxxxxx client_secret: yyyyyyyyy scopes: ['read:user', 'repo'] endpoint: auth_url: 'https://github.com/login/oauth/authorize' token_url: 'https://github.com/login/oauth/access_token' policy: allow: and: - domain: is: company.com ``` ## Step-by-step ### 1. Set up MCP server routes Configure one or more MCP server routes for the tools you want to expose. See [Protect an MCP Server](https://www.pomerium.com/docs/capabilities/mcp/protect-mcp-server.md) or [MCP + Upstream OAuth](https://www.pomerium.com/docs/capabilities/mcp/mcp-upstream-oauth.md). ### 2. Configure the client route Add a route with `mcp.client: {}`. Pomerium passes an External Token (TE) to your app backend in the `Authorization: Bearer` header on every request. ### 3. Discover available MCP servers Your app calls the `/.pomerium/mcp/routes` endpoint to list servers and their connection status: ```http GET https://chat-app.your-domain.com/.pomerium/mcp/routes Authorization: Bearer Accept: application/json ``` **Response:** ```json { "servers": [ { "name": "Database Server", "url": "https://db-mcp.your-domain.com", "connected": true }, { "name": "GitHub", "url": "https://github-mcp.your-domain.com", "connected": false } ] } ``` ### 4. Handle upstream OAuth (if needed) If a server shows `connected: false`, redirect the user to complete upstream authentication: ``` https://github-mcp.your-domain.com/.pomerium/mcp/connect?redirect_url=https://chat-app.your-domain.com/callback ``` The `redirect_url` must match a configured MCP client route host. After authentication, the user is redirected back to your app. ### 5. Pass the token to the LLM Your app backend extracts the External Token (TE) from the request and provides it to the LLM API along with the MCP server URLs. The LLM uses the token to call MCP tools on behalf of the user: ``` Your App → LLM API: "Here are the MCP servers and the user's token" LLM API → Pomerium: tools/call with Authorization: Bearer (TE) Pomerium → MCP Server: Authenticated, authorized request ``` Pomerium validates the token, applies policies (including [tool-level restrictions](https://www.pomerium.com/docs/capabilities/mcp/limit-mcp-tools.md)), and proxies the request. ### 6. Audit tool calls Every MCP tool call is logged by Pomerium with full details. Enable MCP-specific logging to monitor what the LLM does on behalf of your users: ```yaml authorize_log_fields: - request-id - email - mcp-method - mcp-tool - mcp-tool-parameters ``` See [Observability](https://www.pomerium.com/docs/capabilities/mcp/reference.md#observability) for the full logging reference. ## Pattern 2: Service accounts for headless agents For CI pipelines, coding agents, and background jobs where no human is present to complete an interactive login, use a Pomerium [service account](https://www.pomerium.com/docs/capabilities/service-accounts.md). A service account provides a long-lived JWT that authenticates MCP requests directly — no browser required. ### How it works A service account is tied to a specific user in your identity provider. When the service account makes MCP requests, Pomerium evaluates the same identity-based policies (email, groups) as it would for that user's interactive session. For MCP servers that require upstream OAuth (e.g., GitHub, Notion), the service account **reuses upstream tokens that the user has already provisioned** through an interactive session. This means: 1. The user logs in and connects to upstream MCP servers via the **routes portal** 2. An administrator creates a service account for that user 3. The service account can now access those same MCP servers using the cached upstream tokens Service accounts cannot independently initiate upstream OAuth flows. If an upstream connection hasn't been established by the user's interactive session, the service account's request will receive a 401 from the upstream server. The user must hydrate their upstream connections first. ### Step-by-step #### 1. Hydrate upstream connections Log in to Pomerium and visit the **routes portal**. For each MCP server you want the agent to access, click **Connect** to complete the upstream OAuth consent flow. This caches upstream tokens under your user identity. For MCP servers that don't require upstream OAuth (i.e., internal servers behind a [basic MCP route](https://www.pomerium.com/docs/capabilities/mcp/protect-mcp-server.md)), this step is not needed. #### 2. Create a service account Create a service account for your user in the Enterprise Console or Pomerium Zero. The service account must be associated with the same identity (email) that hydrated the upstream connections. See [Service Accounts](https://www.pomerium.com/docs/capabilities/service-accounts.md) for detailed setup instructions. #### 3. Configure your coding agent Pass the service account JWT to your agent as a bearer token. The `Pomerium-` prefix tells Pomerium to authenticate the request as a service account: ``` Authorization: Bearer Pomerium- ``` Below are examples for popular coding agents. In all examples, `POMERIUM_SERVICE_ACCOUNT_JWT` is set to the **raw JWT** (without any prefix). **Claude Code** Add a Pomerium-fronted MCP server to Claude Code using the `--header` flag: ```bash claude mcp add --transport http my-mcp-server \ https://mcp-server.your-domain.com \ --header "Authorization: Bearer Pomerium-${POMERIUM_SERVICE_ACCOUNT_JWT}" ``` Or configure it in `.mcp.json` for a shared project setup: ```json { "mcpServers": { "my-mcp-server": { "type": "http", "url": "https://mcp-server.your-domain.com", "headers": { "Authorization": "Bearer Pomerium-${POMERIUM_SERVICE_ACCOUNT_JWT}" } } } } ``` Claude Code expands `${POMERIUM_SERVICE_ACCOUNT_JWT}` from your shell environment. Set it before starting Claude Code: ```bash export POMERIUM_SERVICE_ACCOUNT_JWT="eyJhbGciOi..." ``` See the [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp) for full configuration options. **OpenAI Codex** Codex uses `bearer_token_env_var` to read a token from an environment variable and sends it as `Authorization: Bearer `. Because Codex adds the `Bearer` prefix automatically, the environment variable must include the `Pomerium-` prefix: ```toml [mcp_servers.my-mcp-server] url = "https://mcp-server.your-domain.com" bearer_token_env_var = "POMERIUM_MCP_TOKEN" ``` ```bash export POMERIUM_MCP_TOKEN="Pomerium-eyJhbGciOi..." ``` See the [Codex MCP documentation](https://developers.openai.com/codex/mcp/) for full configuration options. **Other agents and CI** For any agent or CI system that supports MCP over HTTP, set the full header directly: ```bash curl -H "Authorization: Bearer Pomerium-${POMERIUM_SERVICE_ACCOUNT_JWT}" \ https://mcp-server.your-domain.com/mcp ``` ## Sample repos and next steps - **[pomerium/mcp-app-demo](https://github.com/pomerium/mcp-app-demo)** — Full working Pomerium Chat app with MCP server integration, LLM API calling, and audit logging - [Service Accounts](https://www.pomerium.com/docs/capabilities/service-accounts.md) — Create and manage service accounts - [MCP + Upstream OAuth](https://www.pomerium.com/docs/capabilities/mcp/mcp-upstream-oauth.md) — Configure upstream OAuth for services like GitHub - [Limit MCP Tool Calling](https://www.pomerium.com/docs/capabilities/mcp/limit-mcp-tools.md) — Control which tools the LLM can call - [MCP Full Reference](https://www.pomerium.com/docs/capabilities/mcp/reference.md) — Token types, session lifecycle, configuration details --- ### Develop an MCP App Source: https://www.pomerium.com/docs/capabilities/mcp/develop-mcp-app.md # Develop an MCP App Build interactive apps using the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps) — the open standard for rendering interactive UI components inside MCP hosts. The Pomerium template uses the official [`@modelcontextprotocol/ext-apps`](https://modelcontextprotocol.github.io/ext-apps/api/) SDK and works with any MCP Apps spec-compliant host, including ChatGPT, Claude, VS Code, and Goose. Your MCP server handles tool execution and returns structured data that the host renders as interactive UI components in a sandboxed iframe. **What you get:** - An MCP server that registers tools and returns widget-ready structured data - React-based widgets rendered inside your MCP host as interactive iframes - UI capability negotiation — the server detects host capabilities and falls back to text-only for non-UI clients - Secure authentication via Pomerium — use `pom.run` for local development or deploy permanently ## Architecture The template builds and serves two separate properties: | Property | Dev | Production | Access policy | | --- | --- | --- | --- | | **MCP server** (`/mcp`) | Public URL via `pom.run` tunnel | Public URL via Pomerium | Auth-gated — tool calls always require a Bearer token | | **Widget assets** | `localhost:4444` (rendered in your browser) | Public URL via Pomerium | **Public** — the MCP host renders widgets in a sandboxed iframe and cannot forward credentials | The MCP server always needs a publicly reachable URL so the MCP host can reach it. Widget assets only need a public URL in production; during development your local browser loads them directly from localhost. Tool calls always carry a Bearer token and should be gated by a strict Pomerium policy. Widgets must be publicly accessible because the MCP host renders them in a sandboxed iframe and cannot forward authentication tokens. ```mermaid sequenceDiagram actor U as User participant H as MCP Host participant P as Pomerium participant S as MCP Server (Your App) participant W as Widget (React iframe) U ->> H: "echo hello world" H ->> P: tools/call (Bearer token) P ->> S: Authenticated request (auth-gated route) S ->> P: Response with text + structuredContent + _meta.ui.resourceUri P ->> H: Tool result H ->> W: Render widget with tool output (public route) W ->> U: Interactive UI component ``` ## How it works Your MCP server registers tools using `registerAppTool` from `@modelcontextprotocol/ext-apps/server`. Each tool response includes: 1. **Text content** — human-readable text for the MCP host's conversation 2. **Structured JSON data** — passed to the widget via the `App.ontoolresult` callback 3. **Widget metadata** — a `_meta.ui.resourceUri` pointing to a widget resource (e.g., `ui://echo`) The host renders your widget in an iframe. The widget uses the `App` class from `@modelcontextprotocol/ext-apps` to receive tool output and call back into the MCP server via `app.callServerTool()`. ## Prerequisites - **Node.js 22+** — verify with `node -v` - **npm 10+** — ships with Node 22, verify with `npm -v` - An MCP Apps-compatible host (e.g., ChatGPT, Claude, VS Code, Goose) ## Step-by-step ### 1. Scaffold from the template ```bash git clone https://github.com/pomerium/chatgpt-app-typescript-template my-mcp-app cd my-mcp-app npm install npm run dev ``` This starts both the MCP server (`http://localhost:8080`) and widget dev server (`http://localhost:4444`). ### 2. Expose your MCP server with pom.run The MCP host needs a public URL to reach your server — `localhost` won't work. In a new terminal (keep `npm run dev` running): ```bash ssh -R 0 pom.run ``` Sign in and you'll get a public route URL like `https://mcp.your-route-1234.pomerium.app` that tunnels to your local MCP server. The widget dev server (`localhost:4444`) stays local — your browser loads it directly. For full tunneling details, see [Tunnel to ChatGPT During Development](https://www.pomerium.com/docs/capabilities/mcp/tunnel-to-chatgpt.md). ### 3. Connect to your host Add your public URL + `/mcp` as a connector in your MCP host. For example, in ChatGPT: 1. Go to **Settings → Connectors → Add Connector** 2. Enter your public URL: `https://mcp.your-route-1234.pomerium.app/mcp` 3. Save the connector 4. Start a new chat, add your app, and test with: `echo Hi there!` \[The echo tool rendered as an interactive widget inside ChatGPT] Other hosts (Claude, VS Code, Goose) follow the same pattern — add a connector pointing to your `/mcp` endpoint. ### 4. Build your own tools and widgets The template's echo tool shows the full pattern. The key pieces when adding your own tool: **Register a tool with UI binding** — use `registerAppTool` to declare the tool and its widget in one place: ```typescript registerAppTool( server, 'my_tool', { title: 'My Tool', description: 'Does something cool', inputSchema: { type: 'object', properties: { input: {type: 'string', description: 'Tool input'}, }, required: ['input'], }, _meta: { ui: {resourceUri: 'ui://my-widget'}, }, }, async (args) => { const input = MyToolInputSchema.parse(args).input; return { content: [{type: 'text', text: 'Result'}], structuredContent: {result: input}, }; }, ); ``` **Register a widget resource** — the `text/html;profile=mcp-app` MIME type is required for MCP hosts to render the widget: ```typescript registerAppResource( server, 'ui://my-widget', 'ui://my-widget', {mimeType: RESOURCE_MIME_TYPE}, async () => ({ contents: [ { uri: 'ui://my-widget', mimeType: RESOURCE_MIME_TYPE, // 'text/html;profile=mcp-app' text: await readWidgetHtml('my-widget'), }, ], }), ); ``` **Widget entry point** — React component in `widgets/src/widgets/my-widget.tsx` using the `App` class from `@modelcontextprotocol/ext-apps`: ```tsx function MyWidget() { const [toolOutput, setToolOutput] = useState(null); const [theme, setTheme] = useState('light'); useEffect(() => { const app = new App({name: 'MyWidget', version: '1.0.0'}); app.ontoolresult = (result) => setToolOutput(result.structuredContent ?? null); app.onhostcontextchanged = (context) => setTheme(context?.theme ?? 'light'); app.connect(); }, []); return (

My Widget

{JSON.stringify(toolOutput, null, 2)}
); } const rootElement = document.getElementById('my-widget-root'); if (rootElement) { createRoot(rootElement).render( , ); } ``` The build auto-discovers all files matching `widgets/src/widgets/*.{tsx,jsx}` and bundles them with their mounting code. See the [template README](https://github.com/pomerium/chatgpt-app-typescript-template) for the complete guide: project structure, `App` API reference, display modes, inline widget assets, Storybook, testing, environment variables, and troubleshooting. ## Inline widget assets Some hosts (e.g., Claude) require fully self-contained HTML — external ` ``` ```javascript (async () => { const result = await fetch('https://api.localhost.pomerium.io', { method: 'POST', headers: { Accept: 'application/json', }, }); const json = await result.json(); console.log('RESULT', json); })(); ``` #### API Application ```go package main import ( "io" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") io.WriteString(w, `{ "message": "HELLO WORLD" }`) }) http.ListenAndServe(":8000", nil) } ``` #### Docker-Compose ```yaml services: pomerium: image: pomerium/pomerium:latest ports: - 443:443 volumes: - ./_wildcard.localhost.pomerium.io.pem:/pomerium/cert.pem:ro - ./_wildcard.localhost.pomerium.io-key.pem:/pomerium/private-key.pem:ro - ./config.yaml:/pomerium/config.yaml:ro app: image: golang:latest command: ['go', 'run', '.'] environment: GO111MODULE: 'off' volumes: - ./app.go:/go/app.go:ro - ./index.html:/go/index.html:ro - ./index.mjs:/go/index.mjs:ro api: image: golang:latest command: ['go', 'run', '.'] environment: GO111MODULE: 'off' volumes: - ./api.go:/go/api.go:ro ``` ### Problem When accessing `app.localhost.pomerium.io`, the browser returns a 401 error for requests to `api.localhost.pomerium.io`: > URL: https://api.localhost.pomerium.io/ Status: 401 Unauthorized Source: Network Address: 127.0.0.1:443 Initiator: index.mjs:2 Because these domains differ, cookies may not be automatically sent, leading to authentication failures and CORS-related requests being denied. ### Solutions #### Use a Single Domain Rather than using different subdomains for your app and API, configure them on a single domain with different paths. For example: ```yaml routes: - from: https://app.localhost.pomerium.io prefix: /api to: http://api:8000 allow_any_authenticated_user: true - from: https://app.localhost.pomerium.io to: http://app:8000 allow_any_authenticated_user: true ``` Then your requests can share domain cookies and avoid CORS complexities. Update the JavaScript to reference `location.origin + '/api'` instead of the separate `api.localhost.pomerium.io` domain. #### Pass the Pomerium Credentials via a Header If you must use different domains, you can pass the JWT in a custom header. For example: ```yaml cookie_http_only: false ``` ```javascript (async () => { const result = await fetch('https://api.localhost.pomerium.io', { method: 'POST', headers: { Accept: 'application/json', 'X-Pomerium-Authorization': document.cookie.substring( document.cookie.indexOf('=') + 1, ), }, }); const json = await result.json(); console.log('RESULT', json); })(); ``` #### Share the Cookie If all your Pomerium routes share a parent domain, you can use the [Cookie Domain](https://www.pomerium.com/docs/reference/cookies.md#cookie-domain) setting to share a single session cookie across all routes. This option should not be used if you have a mix of sub-domains where some resolve to Pomerium and some do not. Doing so could expose the Pomerium session cookie to upstream services, which could result in session hijacking if an upstream service were to be compromised. In this example, you can set `cookie_domain: '.localhost.pomerium.io'` to share the Pomerium session cookie across both `api.localhost.pomerium.io` and `app.localhost.pomerium.io`. You'll still need to configure your JavaScript client to send credentials: ```javascript (async () => { const result = await fetch('https://api.localhost.pomerium.io', { method: 'POST', headers: { Accept: 'application/json', }, credentials: 'include', }); const json = await result.json(); console.log('RESULT', json); })(); ``` > For more details on how cookies and credentials work in fetch or XHR, refer to [XMLHttpRequest/withCredentials on MDN](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials) and [fetch() API documentation](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). #### Set up Additional Login Redirects You can use the [Additional Login Redirect Hosts](https://www.pomerium.com/docs/reference/routes/additional-login-redirect-hosts.md) option to change the login behavior for `app.localhost.pomerium.io` so that logging in at this domain will automatically set a Pomerium session cookie for `api.localhost.pomerium.io` as well: ```yaml routes: - from: https://api.localhost.pomerium.io to: http://api:8000 allow_any_authenticated_user: true cors_allow_preflight: true set_response_headers: 'Access-Control-Allow-Credentials': 'true' 'Access-Control-Allow-Origin': 'https://app.localhost.pomerium.io' 'Access-Control-Allow-Headers': 'X-Pomerium-Authorization' - from: https://app.localhost.pomerium.io to: http://app:8000 allow_any_authenticated_user: true depends_on: - api.localhost.pomerium.io ``` As in the previous approach, the JavaScript `fetch()` request will still need to set `credentials: 'include'` to make an authenticated request. --- ### Additional API and Internals - [Certificates and TLS with Pomerium](https://www.pomerium.com/docs/internals/certificates-and-tls.md): Learn how to configure Pomerium for both downstream and upstream TLS, including mutual authentication with custom certificate authorities. - [Client bindings](https://www.pomerium.com/docs/internals/client-binding.md): This document describes bindings to non-HTTP clients in Pomerium - [Clusters for Zero](https://www.pomerium.com/docs/internals/clusters.md): This document describes Clusters in Pomerium Zero. - [Clusters for Enterprise](https://www.pomerium.com/docs/internals/clusters-for-enterprise.md): This document describes Clusters in Enterprise. - [Persistence](https://www.pomerium.com/docs/internals/data-storage.md): This article describes Pomerium's data storage requirements and backends - [Pomerium Health Checks](https://www.pomerium.com/docs/internals/health-checks.md): Learn how to configure health checks for Pomerium deployments - [Pomerium Enterprise API](https://www.pomerium.com/docs/internals/management-api-enterprise.md): Learn how to programmatically manage Pomerium Enterprise using the gRPC API available in both Python and Go libraries. - [Get Started With the Pomerium Zero API](https://www.pomerium.com/docs/internals/management-api-zero.md): This page shows you how to create a route and policy with the Pomerium Zero API. - [Telemetry & Metrics in Pomerium](https://www.pomerium.com/docs/internals/metrics.md): Learn how Pomerium collects and displays metrics in Pomerium Zero and Pomerium Enterprise. - [Mutual Authentication](https://www.pomerium.com/docs/internals/mutual-auth.md): Learn how Pomerium supports mutual authentication and can help you incorporate mTLS into your security framework. - [Namespaces](https://www.pomerium.com/docs/internals/namespacing.md): This page describes the concept of namespaces, why they're important for enabling self-service, and how they enforce RBAC. - [Pomerium's Security Policy & Threat Model](https://www.pomerium.com/docs/internals/security.md) - [Sessions](https://www.pomerium.com/docs/internals/sessions.md): This article describes how sessions work in Pomerium. - [Special Routes](https://www.pomerium.com/docs/internals/special-routes.md): Learn about Pomerium's reserved endpoints that handle authentication, session management, user information, health checks, and discovery. - [Zero trust](https://www.pomerium.com/docs/internals/zero-trust.md) ## Training - [Pomerium Zero Fundamentals](https://www.pomerium.com/docs/courses.md): Welcome to Pomerium Fundamentals, a series of courses designed to teach you the fundamentals of Pomerium so you can secure your applications with confidence. ## Other - [Pomerium API](https://www.pomerium.com/docs/api/pomerium-api.md): API for managing Pomerium configuration. - [CreateKeyPair](https://www.pomerium.com/docs/api/pomerium-config-config-service-create-key-pair.md): Creates a key pair - [CreatePolicy](https://www.pomerium.com/docs/api/pomerium-config-config-service-create-policy.md): Creates a policy - [CreateRoute](https://www.pomerium.com/docs/api/pomerium-config-config-service-create-route.md): Creates a route - [CreateServiceAccount](https://www.pomerium.com/docs/api/pomerium-config-config-service-create-service-account.md): Creates a service account - [DeleteKeyPair](https://www.pomerium.com/docs/api/pomerium-config-config-service-delete-key-pair.md): Deletes a key pair - [DeletePolicy](https://www.pomerium.com/docs/api/pomerium-config-config-service-delete-policy.md): Deletes a policy - [DeleteRoute](https://www.pomerium.com/docs/api/pomerium-config-config-service-delete-route.md): Deletes a route - [DeleteServiceAccount](https://www.pomerium.com/docs/api/pomerium-config-config-service-delete-service-account.md): Deletes a service account - [GetKeyPair](https://www.pomerium.com/docs/api/pomerium-config-config-service-get-key-pair.md): Gets a key pair - [GetPolicy](https://www.pomerium.com/docs/api/pomerium-config-config-service-get-policy.md): Gets a policy - [GetRoute](https://www.pomerium.com/docs/api/pomerium-config-config-service-get-route.md): Gets a route - [GetServerInfo](https://www.pomerium.com/docs/api/pomerium-config-config-service-get-server-info.md): Gets information about a server - [GetServiceAccount](https://www.pomerium.com/docs/api/pomerium-config-config-service-get-service-account.md): Gets a service account - [GetSettings](https://www.pomerium.com/docs/api/pomerium-config-config-service-get-settings.md): Gets settings - [ListAvailableLogFields](https://www.pomerium.com/docs/api/pomerium-config-config-service-list-available-log-fields.md): Lists all the available log fields - [ListKeyPairs](https://www.pomerium.com/docs/api/pomerium-config-config-service-list-key-pairs.md): Lists key pairs - [ListPolicies](https://www.pomerium.com/docs/api/pomerium-config-config-service-list-policies.md): Lists policies - [ListRoutes](https://www.pomerium.com/docs/api/pomerium-config-config-service-list-routes.md): Lists routes - [ListServiceAccounts](https://www.pomerium.com/docs/api/pomerium-config-config-service-list-service-accounts.md): Lists service accounts - [ListSettings](https://www.pomerium.com/docs/api/pomerium-config-config-service-list-settings.md): Lists settings - [UpdateKeyPair](https://www.pomerium.com/docs/api/pomerium-config-config-service-update-key-pair.md): Updates an existing key pair - [UpdatePolicy](https://www.pomerium.com/docs/api/pomerium-config-config-service-update-policy.md): Updates an existing policy - [UpdateRoute](https://www.pomerium.com/docs/api/pomerium-config-config-service-update-route.md): Updates an existing route - [UpdateServiceAccount](https://www.pomerium.com/docs/api/pomerium-config-config-service-update-service-account.md): Updates an existing service account - [UpdateSettings](https://www.pomerium.com/docs/api/pomerium-config-config-service-update-settings.md): Updates existing settings - [Documentation Versions](https://www.pomerium.com/docs/versions.md) --- ## How to Use These Docs Last-Updated: 2026-08-25 This documentation is publicly available and approved for LLM training and reference. | Resource | URL | Size | Use it for | |----------|-----|------|------------| | Navigator | https://www.pomerium.com/llms.txt | ~14KB | Quick orientation and curated links | | Context bundle | https://www.pomerium.com/llms-full.txt | ~99K tokens | Key docs inline — start here for most questions | | Full index | https://www.pomerium.com/llms-index.txt | ~42KB | Exhaustive page discovery | | Individual page | Append `.md` to generated `/docs/` page URLs (excluding API pages) | varies | Deep-dive on a specific topic | Cite only `www.pomerium.com` docs. Do not cite `docs.pomerium.com` or archive hosts.