> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/access/content-providers-and-uri-patterns.md).

# Content providers and URI patterns

How OpenIAM decides which content provider and URI pattern answer a request, what the entitlement check really checks, and the three inheritance rules that surprise people.

## What this is, and when you need it

A **content provider** is a browser-visible host. It says: requests arriving at this domain (optionally this scheme) belong to this application context, authenticate against this provider, carry this session cookie, and are laid out with this theme. A **URI pattern** is a path within that host, and it says what happens to matching requests — whether they are public, what authenticates them, where they are proxied, what is injected into the upstream request, and what is stripped out.

Together they decide **what URLs exist and what protects them**, which makes this the highest-risk configuration in the guide. Read this page once and you will be able to predict which provider and which pattern answer a given URL; explain why a pattern you saved has no effect; tell an entitlement failure apart from an authentication failure; and know which of a pattern's settings replace the content provider's rather than adding to them.

This page is the model, not a procedure. The procedures live on the gateway pages, listed under [Where to go next](#where-to-go-next); every field on both screens is in [Content provider and URI pattern reference](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md).

{% hint style="danger" %}
Before you edit either object, establish and test a break-glass administrator in a separate browser session — see [Change configuration safely](/administration-guide/start/change-safely.md#keep-a-way-back-in). A broken content provider on a system whose sign-in routing you have just changed is a database-level recovery.
{% endhint %}

## The two objects, and which one owns what

| Decision                                                | Owned by                                                     |
| ------------------------------------------------------- | ------------------------------------------------------------ |
| Which host and scheme this configuration claims         | Content provider (`DOMAIN_PATTERN`, `IS_SSL`)                |
| Which authentication provider identifies the user       | Content provider, overridable per pattern                    |
| The session cookie's name and domain                    | Content provider only                                        |
| Where an unauthenticated request is sent                | Content provider (`LOGIN_URL`)                               |
| Which authentication rules a request must satisfy       | Content provider, **replaced** by any rule on the pattern    |
| Which upstream server the request is proxied to         | Content provider's server set, overridable per pattern       |
| Which path the upstream is asked for                    | Pattern (`proxyPassLocation`, labelled **Application Path**) |
| Whether the URL is anonymous, and who is entitled to it | Pattern, plus a resource per HTTP method                     |
| What is injected into or stripped from the request      | Pattern, or one HTTP method of it                            |
| What is rewritten in the response body                  | Pattern                                                      |
| The stylesheet and logos on the rendered page           | Pattern's theme if set, otherwise the content provider's     |

## How routing resolves

Resolution happens in three stages, in the ESB, against a cache — not per request in the database. The cache is rebuilt on a five-minute sweep, which is why every save on these screens says *"Please allow a few minutes for changes to propagate."* Take that literally: a change you cannot see yet is usually a change that has not swept yet, not a change that failed.

### Stage one: host to content provider

OpenIAM indexes every content provider by its `DOMAIN_PATTERN` and looks it up by the request's host — plus `:` and the port, but **only when the request URI carries an explicit port**. So a provider whose domain pattern is `idm.example.com` matches `https://idm.example.com/…`, and a provider registered as `localhost:8080` matches only requests that name that port.

Where several providers share a host, OpenIAM walks them in descending SSL order and takes the first whose SSL flag is compatible with the request's scheme:

| The provider's `IS_SSL` | The request | Result                           |
| ----------------------- | ----------- | -------------------------------- |
| `Y`                     | `https`     | match                            |
| `Y`                     | `http`      | keep looking                     |
| `N`                     | `http`      | match                            |
| `N`                     | `https`     | keep looking                     |
| `NULL`                  | either      | match — unset means "don't care" |

So a provider with `IS_SSL` unset matches both schemes, and is the reason two providers on one host can behave unpredictably if neither sets the flag. The shipped `DEFAULT_CP` is one of these: it is seeded with `IS_SSL` null. Because the unique key is `(DOMAIN_PATTERN, IS_SSL)` you can have exactly one HTTP, one HTTPS and one don't-care provider per host — confirm it by trying to save a second provider on a hostname you already use with the same **Supports SSL** setting, and reading the refusal.

### Stage two: path to URI pattern

OpenIAM registers every one of the provider's patterns **lower-cased** and looks the incoming path up lower-cased as well. A blank path is treated as `/`. Routing then resolves the **most specific** registered pattern, so `/webconsole/editRole` beats `/webconsole/*`.

Two consequences that are not obvious:

**Patterns are case-insensitive.** Both registration and lookup lower-case the string, so you cannot have `/App` and `/app` behave differently, and case is never the explanation for a pattern that does not match.

**A pattern's wildcard placement is validated, but not at save time.** A pattern is accepted only if it contains no `*` at all, or starts with `*` and does not end with one, or ends with `*`. Anything else is **logged and skipped** when the routing cache is built, with an `InvalidPatternException` and the line *"URI Pattern %s for CP %s not valid"* in the ESB service log. So `/app/*/admin` saves happily and then silently does not exist. After saving any pattern containing a `*`, search the service log for that line before you trust the pattern; the save confirmation does not tell you.

### Stage three: query string and HTTP method

Having found the pattern, OpenIAM decides whether it actually matches this request's query string and HTTP method. If the pattern has methods defined and the request supplied one of them, the method's **Match Mode** decides; otherwise the pattern's own Match Mode does. The four values, in increasing specificity, are `IGNORE`, `NO_PARAMS`, `ANY_PARAMS` and `SPECIFIC_PARAMS`, and they are described with their on-screen labels in [Match Mode values](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md#match-mode-values). `IGNORE` is what every shipped pattern uses, and what you want unless you are deliberately matching on parameters.

Where a request could match several of a pattern's methods, the most specific match is the one that declares parameters: with `SPECIFIC_PARAMS` and a comparable method on a parent URI, OpenIAM collects every matching method and takes the first. If you rely on that, declare the specific method on the exact pattern rather than a parent, so the outcome does not depend on collection order.

A null Match Mode is impossible through the Webconsole — the column is `NOT NULL` — but a direct database edit that leaves it empty makes routing fail at request time with *"URI Pattern %s has a null matching mode"* in the ESB service log rather than at save time.

## What the entitlement check actually checks

The public flags and the entitlement checks interact in a way that is worth having exactly right, because two of the four combinations are not what people assume.

| Situation                                                      | What OpenIAM requires                                                                                                                                 |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| No pattern matched the path at all                             | The request is refused as not found. There is no fallback to the content provider's resource                                                          |
| Pattern is public                                              | Nothing. The content provider's own resource is **not** checked either                                                                                |
| Pattern is not public                                          | Entitlement to the pattern's `URL_PATTERN` resource; then, if the content provider is also not public, entitlement to the provider's resource as well |
| The request matched one of the pattern's declared HTTP methods | Entitlement to that method's `URI_PATTERN_METHOD` resource — **always**, whether or not the pattern is public                                         |

The last row is the one that catches people, and the Webconsole says so in the section's own help text: *"This resource controls access to the URI Pattern for the specified HTTP Method. If a user is entitled to this resource, they have permission to access the URI Pattern using this HTTP Method. This entitlement overrides the entitlement to the global URI Pattern resource."* So declaring an HTTP method on a public pattern makes that method non-public until somebody is granted the method's resource. Declare methods deliberately, and grant their resources at the same time.

Authentication is a separate question from all of this. The pattern's authentication rules decide *who gets challenged*; the resources above decide *who is entitled*. A request can be fully authenticated and still refused, and a public pattern is reachable by an anonymous request no matter who is entitled to anything.

## Three inheritance rules that surprise people

Each of these is a case of "I set it and nothing happened", and each has a different cause.

**Authentication rules replace, they do not add.** If the pattern has any rule at all under **Supported Authentication Levels**, OpenIAM evaluates that list and never looks at the content provider's **Supported Authentication Rules**. Only a pattern with an empty list inherits, and the screen says so: *"The authentication grouping from the parent content provider will be used"*. So attaching one rule to a pattern does not add a step on top of the site's authentication — it becomes the whole of that pattern's authentication. Rules within a pattern are ordered, and you reorder them by dragging. Binding a rule to one URL is worked through in [Demand a different authentication on one URL](/administration-guide/gateway/protect-an-application.md#demand-a-different-authentication-on-one-url).

**Themes resolve per matched pattern.** The pattern's **UI Theme** applies if set; otherwise the content provider's does; otherwise no theme is applied. Because resolution happens per pattern, the theme that brands a page is the theme of whichever pattern matched that page's URL — which is why a login page can end up unbranded while the rest of the site is branded, if a pattern in between carries a theme of its own or the provider's theme was never set. What a theme actually contains, and why the stylesheet URL must be publicly reachable, is in [UI themes and the stylesheet URL](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md#ui-themes-and-the-stylesheet-url).

**A static redirect deletes the Groovy one.** Saving a pattern with anything in **Redirect URL** clears **Redirect URL Groovy Script**, and at request time the static value is the one used. The two fields are not a fallback pair, and older OpenIAM documentation states the precedence the other way round. **Redirect URL** is validated on save — it must begin with `/`, `http` or `https` — and the Groovy script is validated as instantiable, so both failures happen on the screen rather than at request time.

There is a fourth of the same shape, on metadata rather than on the pattern: method-level metadata replaces pattern-level metadata for requests using that method, rather than adding to it. See [Metadata: what belongs on this page, and what does not](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md#metadata-what-belongs-on-this-page-and-what-does-not).

## Why an exact duplicate pattern is rejected

`URI_PATTERN` is unique on `(CONTENT_PROVIDER_ID, PATTERN)`, so a second pattern with the same string under the same content provider is refused with `URI_PATTERN_EXISTS` (error 129) — *"URI Pattern with the specified pattern already exists for Content Provider"*. The same string **is** allowed under a different content provider, which is how one path can behave differently per host.

Two consequences for design. Because the unique key is on the literal string, `/app/*` and `/app/**` are two different rows — and only one of them is a valid pattern. And because you cannot have two rows for the same path, per-method behaviour has to go on the single pattern's **Supported HTTP Methods**, not in a second pattern. All four uniqueness constraints and their exact error text are in [Content provider and URI pattern reference](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md#uniqueness-constraints-and-their-error-text).

## Menus

Webconsole and SelfService navigation entries are resources of type `MENU_ITEM`, organised as trees rooted at the value of `org.openiam.ui.landingpage.menu.name` — `IDM` in the Webconsole's properties and `SELFSERVICE` in SelfService's. Edit them at `/webconsole/menus` and `/webconsole/editMenu`; `/webconsole/getCurrentMenuTreeForPrincipal` returns the tree as the signed-in user sees it, which is the fastest way to check what a delegated administrator can reach.

**Menu visibility is not authorization.** Granting a role a `MENU_ITEM` resource makes the entry appear; it does not authorize the operation behind it, which is a separate `URL_PATTERN` or `URI_PATTERN_METHOD` resource evaluated independently. Both directions of the mistake are real: a hidden menu whose operation is still reachable, and a visible menu whose action returns an error. Grant both, and test one permitted and one denied action.

Menu nodes carry two flags that are easy to confuse. `IS_VISIBLE='N'` hides the entry from everyone. The `MENU_IS_PUBLIC` resource property set to `true` makes it available without an entitlement — check the children too, since a public parent with protected children reads as broken to users.

Each Webconsole screen finds its own menu root through its own property, so renaming a menu resource without changing the matching property makes that screen's menu disappear. The sixteen properties and their shipped values are in [Menu root properties](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md#menu-root-properties).

## When it goes wrong

Symptoms that are the *routing model's* fault. Symptoms belonging to injection, rewriting or the proxy itself are on the gateway pages.

| Symptom                                                                                                               | Cause                                                                                                            | Fix                                                                                                       |
| --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| A pattern has no effect at all                                                                                        | The wildcard is in the middle, so the pattern failed validation and was skipped when the routing cache was built | Move the `*` to the start or the end; check the ESB service log for *"URI Pattern … not valid"*           |
| A change has not taken effect                                                                                         | The routing cache sweeps every five minutes                                                                      | Wait and re-test in a fresh browser session; a successful save needs no restart                           |
| The wrong provider answers for a host                                                                                 | Two providers share the host and at least one has `IS_SSL` unset (matches both schemes)                          | Set `IS_SSL` explicitly on every provider; the unique key is `(DOMAIN_PATTERN, IS_SSL)`                   |
| A general pattern is matching instead of a specific one                                                               | Routing picks the most specific *valid* pattern, so the specific one either did not save or is invalid           | Look for a rejected wildcard in the ESB service log; case is not the cause, since matching is lower-cased |
| Pattern matches with no query string but not with one, or vice versa                                                  | `NO_PARAMS` vs `ANY_PARAMS`                                                                                      | Use `IGNORE` unless you need parameter matching                                                           |
| ESB service log: *"URI Pattern … has a null matching mode"*                                                           | `PARAM_MATCH_MODE` is null, which the column forbids — a direct database edit                                    | Set **Match Mode** to one of the four values                                                              |
| An entitled user is refused on one HTTP method only                                                                   | The method's `URI_PATTERN_METHOD` resource is checked even when the pattern is public                            | Grant the method's resource, or remove the method declaration                                             |
| Cannot save a second pattern for the same path                                                                        | `URI_PATTERN_UNIQUE (CONTENT_PROVIDER_ID, PATTERN)`; error 129                                                   | Put per-method behaviour on the existing pattern's methods, not in a second pattern                       |
| Cannot save the provider: domain exists                                                                               | `UNIQUE_CP_PATTERN (DOMAIN_PATTERN, IS_SSL)`; error 124                                                          | Change the SSL flag or the domain pattern                                                                 |
| An authentication rule attached to a pattern seems to have replaced the site's authentication rather than added to it | It has. A pattern with any rule of its own ignores the content provider's rules entirely                         | Express the whole requirement in the pattern's rules; an empty list is what inherits                      |
| A theme is set on the content provider but one page is unbranded                                                      | Theme resolution is per matched pattern                                                                          | Find the pattern the URL resolves to with the URI Debug Tool, and set or clear the theme there            |
| Menu appears but the action fails                                                                                     | Menu visibility is not authorization                                                                             | Grant the `URL_PATTERN` or `URI_PATTERN_METHOD` resource as well                                          |
| Entitling the password-validation pattern affected the SAML SP pattern                                                | `PWD_VALIDATION_PATTERN` is seeded pointing at `SP_PATTERN`'s resource                                           | Point it at its own resource before entitling either                                                      |
| Locked out after a change                                                                                             | The provider, pattern or authentication rule you edited governs your own sign-in                                 | The break-glass session you tested first; otherwise this is database-level recovery                       |

Two shipped tools answer the first question you will have. The **URI Debug Tool** at `/webconsole-am/federateURIDebug.html` tells you which provider and pattern a URL resolves to; the **Principal Debug Tool** at `/webconsole-am/proxyURIPrincipalDebug.html` tells you which identity the upstream is being given.

## Where to go next

| I want to…                                                                  | Go to                                                                                                                                                |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Publish an application behind the gateway, end to end                       | [Protect an application behind the Access Gateway](/administration-guide/gateway/protect-an-application.md)                                          |
| Require stronger authentication on one URL                                  | [Demand a different authentication on one URL](/administration-guide/gateway/protect-an-application.md#demand-a-different-authentication-on-one-url) |
| Send the application the authenticated identity, in a header or a form post | [Give the application the authenticated identity](/administration-guide/gateway/inject-identity.md)                                                  |
| Rewrite URLs in the response, redirect a path, or brand the pages           | [Advanced request and response transformations](/administration-guide/gateway/request-and-response-transformations.md)                               |
| Look up a field, a shipped default, or the seeded patterns                  | [Content provider and URI pattern reference](/administration-guide/access/content-providers-and-uri-patterns/content-provider-reference.md)          |
| Understand what an authentication rule contains                             | [Step-up authentication](/administration-guide/security/step-up-authentication.md)                                                                   |
| See where these two objects sit in the request path                         | [How the Access Gateway works](/administration-guide/gateway/access-gateway.md)                                                                      |

## Related

* [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md) — the provider a content provider points at
* [Resources and organizations](/administration-guide/access/resources-and-organizations.md) — the `CONTENT_PROVIDER`, `URL_PATTERN` and `URI_PATTERN_METHOD` resource types
* [Change configuration safely](/administration-guide/start/change-safely.md) — break-glass, and why this page insists on it

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/access/content-providers-and-uri-patterns.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
