Virtual hosts
Contents
Expose Universal APIs, Admin, Sites or Webapps on custom domains and paths.
| Vhosts apply to XP’s web endpoint (defaults to port 8080) unless a mapping names another endpoint. A mapping may also target the management or statistics endpoint, bringing ID provider authentication with it. |
Configuration file
com.enonic.xp.web.vhost.cfg
XP’s web port (default: 8080) provide the following services: /api, /admin, /webapp and /site. Use Vhosts to route traffic for specific domains and url patterns to one of these services.
I.e. example.com -> :8080/site/default/master/homepage
Virtual hosts are automatically updated upon change.
enabled = true
mapping.myapp.host = company.com
mapping.myapp.source = /app
mapping.myapp.target = /webapp/name.of.my.app
mapping.myapp.idProvider.myldap = default
| Once vhosts are enabled, unmapped endpoints are no longer accessible. |
- enabled
-
turns on or of vhosts, enabled = false should only be used for development purposes
- host
-
specifies the hostname (aka domain) the vhost will handle
- source
-
refers to basepath used in request, sample above handles company.com/app
- target
-
is the internal route in XP to the specific endpoint/service
- order
-
optionally sets the mapping’s priority when several mappings match a request - lower values are preferred. Unset means least preferable.
- idProvider
-
optionally adds one or more idProviders to the vhost.
idProvidermust be followed by the name of an existing idProvider. The example above refers to the idProvider calledmyldap. Supported values aredefaultorenabled. Only one entry may usedefault. Settings of the ID provider on this vhost follow as sub-keys, see ID provider settings. - endpoint
-
optionally targets the mapping at the management or statistics endpoint instead of the web endpoint.
- allow
-
optionally restricts the vhost to a list of principals.
Each mapping must define a unique name to separate the mappings when multiple mappings exist in the same file. In the example above myapp is used. |
mapping.website.host = example.com
mapping.website.source = /
mapping.website.target = /site/default/master/website
mapping.website.idProvider.adfs = default
mapping.website.idProvider.system = enabled
mapping.admin.host = example.com
mapping.admin.source = /admin
mapping.admin.target = /admin
mapping.admin.idProvider.system = default
Domains
In the definition, Domains can be a single host, multiple hosts separated by space and/or regular expressions (started with ~ symbol).
| IDN hosts should be specified using Punycode representation. |
Request Matching
VHost matches the request if host value matches the requested host AND request-uri matches source.
Host matching can be an exact match or regular expression match. Read more about Java Regular Expressions at Pattern JavaDoc Host matching is case-insensitive in both cases.
For backwards compatibility with old versions of XP root domain is not normalized in host. Specify both host values mapping.example.host = example.com. example.com if case you need to support FQDN-style host. |
host mapping match both www.example.com and example.com hosts
mapping.example.host = www.example.com example.com
host mapping will match any sub-domain: 1.example.com, 2.example.com hosts (but not sub-sub-domains)
mapping.example.host = ~[^.]+\\.example\\.com
Source matching is done with "exact" or "starts-with" match. Source matching is case-sensitive, and is done before URI decoding.
mapping.example.source = /
/hello or starts with /hello/
mapping.example.source = /hello
If multiple VHosts match the request, disambiguation rules apply in the following order:
-
VHost with lower
ordervalue is preferred. Iforderis not specified, order is considered least preferable (Integer.MAX_VALUE). -
VHost with the longest
sourcevalue is preferred. -
arbitrary VHost is selected.
Capture Groups
When VHost matched by a host regular expression, capture groups can be used in target via group references.
mapping.example.host = ~example\\.(?<tld>[^.]+)
mapping.example.target = /site/default/master/${tld}
In this example if host is example.no, then target becomes /site/default/master/no
Avoid mixing regular expressions, multiple hosts and capture groups. If host is not matched by regular expression, then group references in target will remain as-is. |
ID provider settings
What an ID provider may do on a vhost is configured under its entry, so the same ID provider can offer interactive login on one host and only accept tokens on another.
mapping.admin.host = admin.example.com
mapping.admin.source = /
mapping.admin.target = /admin
mapping.admin.idProvider.system = default
mapping.admin.idProvider.system.flows = login,logout,device
mapping.admin.idProvider.system.scope.content\:read.roles = role:cms.project.site.viewer
mapping.admin.idProvider.system.scope.content\:write.roles = role:cms.project.site.editor
mapping.admin.idProvider.system.scope.profile.required = true
mapping.api.host = api.example.com
mapping.api.source = /
mapping.api.target = /api
mapping.api.idProvider.system = default
mapping.api.idProvider.system.flows = autologin
mapping.api.audience = api
-
idProvider.<name>.flows -
The authentication flows the ID provider may use here, see ID provider flows.
deviceis device login, where a user approves a client;autologinalso covers XP’s own access tokens on the declared audience. -
idProvider.<name>.scope.<id>.roles -
A scope a client may ask for in device login on this vhost, and the roles it implies, comma-separated. An empty value declares a scope with no roles, a label for applications. Only roles may be listed. The id is everything between
scope.and the last dot, so it may contain dots; escape colons in it as shown. See scopes. -
idProvider.<name>.scope.<id>.required -
truewhen the user must keep the scope: a client that asks for it gets it with the rest, or the request is denied as a whole. Defaults tofalse. A scope declared by this line alone is a label-only scope. - audience
-
The audiences this vhost serves, comma-separated: an access token is accepted here only when its
audclaim contains one of them. A vhost that declares no audience accepts only tokens whose audience is XP’s own issuer name (token.issuerincom.enonic.xp.security.cfg); that is what a token obtained without aresourcecarries. The audiences a client may ask for are the union of every vhost’s list.
Virtual Host Context
The virtual host context is a set of attributes that can be used to configure the behavior of controllers and other components within the context of a specific virtual host.
Virtual hosts may define context attributes.
mapping.adm.host = example.no
...
mapping.adm.context.my.timeZone = Europe/Oslo
Access to the context attributes is done via the contextLib library.
const contextLib = require('/lib/xp/context');
exports.GET = () => {
const timeZone = contextLib.get()['attributes']['my.timeZone'];
return {
body: {
timeZone: timeZone
}
};
}
It is also possible to set default context repository and branch for the virtual host.
mapping.adm.host = example.com
...
mapping.adm.com.enonic.xp.repository.RepositoryId = com.enonic.cms.repository.myproject
mapping.adm.com.enonic.xp.branch.Branch = master
Management API policy
On a management mapping, the api. context namespace selects what the management resources expose through it. Each resource reads its own settings from the request context and enforces them; a setting’s meaning is defined by the resource. Every resource understands verbs: the comma separated operations the mapping exposes.
mapping.backup.host = backup.example.com
mapping.backup.source = /
mapping.backup.target = /
mapping.backup.endpoint = management
mapping.backup.allow = user:system:backup-agent
mapping.backup.context.api.rest.enabled = false (1)
mapping.backup.context.api.server\:snapshot.verbs = list, create (2)
mapping.backup.context.api.server\:dump.verbs = list, create
mapping.backup.context.api.server\:task.verbs = * (3)
| 1 | Switches the legacy REST endpoints (repo/snapshot/list, system/dump, …) off for this mapping, leaving only the /<application>:<api> resources. Default: enabled. |
| 2 | server:snapshot answers list and create on this mapping and refuses restore and prune with 403. Note the escaped colon: the file is read as a Java properties file, where an unescaped colon ends the key. |
| 3 | * exposes every verb. A resource without a verbs setting is unrestricted: server:app, server:index, server:system, … remain fully available on this mapping. mapping.backup.context.api.server\:app.verbs = - exposes no verb of a resource. |
A path alias (/server:system/vacuum for /server:system/prune, /server:app/installUrl for /server:app/pull) belongs to the verb it aliases and is exposed exactly when that verb is.
verbs decides only whether a verb is reachable through the mapping. What a verb is allowed to do is configured by the resource itself and is not a virtual host setting: server:app pull, for example, enforces the allow-list and checksum policy in com.enonic.xp.server.management.app.cfg on every mapping that exposes it.
Keys under api. are validated when the file is loaded. A key that is neither api.rest.enabled nor api.<application>:<api>.<setting> (typically a colon that was not escaped) is a configuration error and the file is rejected.
Resources may define further settings in the same namespace. A Universal API of an installed application mounted on the management endpoint reads its own api.<application>:<api>.<setting> attributes from the context with contextLib - see Universal APIs on the management endpoint.
API locations
APIs may be served from somewhere other than the address a request arrives on: a dedicated API host, a shorter path, or a media host. The context of a virtual host mapping declares where, for requests it matches:
mapping.example.host = www.example.com
mapping.example.source = /
mapping.example.target = /site/myproject/master/mysite
mapping.example.context.portal.apiBaseUrl = https://apis.example.com (1)
mapping.example.context.portal.apiBaseUrl.media\:image = https://images.example.com (2)
| 1 | Root of the full API set. The API descriptor is appended, so an URL for com.enonic.app.myapp:myapi becomes https://apis.example.com/com.enonic.app.myapp:myapi. |
| 2 | Root of a single API, used as it is - nothing is appended. Note the escaped colon: the configuration file is read as a properties file, where an unescaped colon ends the key. |
Values are used verbatim and decide the URL form themselves - another host, a path on the same host, or a root-relative path - so an API can be exposed anywhere, including on a host that serves nothing else.
Declarations are consulted in order of how specifically they name an API, so exposing APIs somewhere does not silently move APIs that are configured elsewhere:
-
portal.apiBaseUrl.<application>:<api>- names that API. -
media.defaultBaseUrl - names the media APIs.
-
portal.apiBaseUrl- names any API.
An API mounted on the site, webapp or admin tool that serves the request keeps its URL under that mount’s _ endpoint; the declared locations apply to APIs that are addressed from elsewhere.
ID provider flows
The flows an ID provider serves on a vhost can be restricted with the provider’s flows setting:
mapping.example.idProvider.myidprovider = default
mapping.example.idProvider.myidprovider.flows = login,autologin
XP manages three flows:
- login
-
interactive login: the login page and 401 handling. Web endpoint only.
- autologin
-
non-interactive login, e.g. token or basic authentication.
- logout
-
the logout endpoint.
Any other name in the list addresses an additional flow of the ID provider application itself - for instance, the Standard ID Provider serves basic authentication under the name basic, and an ID provider may serve its device authorization endpoints under a name like device. See the ID provider’s own documentation for the flows it supports.
Without a flow list, no restriction applies: the ID provider serves whatever flows it supports. An explicit list serves only what it names - flows = autologin on the system ID provider keeps service account JWTs working but turns basic authentication off. Flow names are not validated: any name an ID provider app understands is fair game.
Management and statistics endpoints
A mapping applies to the web endpoint unless another endpoint is named explicitly:
mapping.stats.host = stats.example.com
mapping.stats.source = /
mapping.stats.target = /
mapping.stats.endpoint = statistics
mapping.stats.idProvider.myidprovider = default
mapping.stats.idProvider.myidprovider.flows = autologin
mapping.stats.allow = role:system.admin
endpoint accepts management (the management API, port 4848) or statistics (the statistics API, port 2609). An unknown value fails the configuration.
Mappings never apply across endpoints: a web mapping stays on the web endpoint. An endpoint no mapping names keeps behaving as if vhosts were not configured; once at least one mapping names the management or statistics endpoint, requests there whose host matches none of its mappings are rejected with 404.
Only non-interactive authentication (the autologin flow) works on the management and statistics endpoints - there is no login page to redirect to. |
A management mapping selects what it exposes with the management API policy in its context.
Allow list
allow restricts who may pass through the vhost. The list is checked after authentication and before the request reaches anything else: unauthenticated requests are rejected with 401, authenticated ones without any of the listed principals with 403.
mapping.example.allow = role:system.admin, role:myapp.analysts
Without allow, no restriction applies. An invalid principal key fails the configuration. The ID provider endpoints are exempt from the check, so interactive login stays reachable.
Examples
"Example Inc", the owner of "example.com" just finished building their new site. The published site is available on <host>:8080/site/default/master/homepage.
In this case, a vhost configuration looking like this will get the job done:
enabled = true
mapping.example.host = example.com
mapping.example.source = /
mapping.example.target = /site/default/master/homepage
After saving the vhost config file, you should see the following line the XP log:
2019-05-10 11:34:17,234 INFO c.e.x.w.v.i.c.VirtualHostConfigImpl - Virtual host is enabled and mappings updated.
Each mapping must have a unique mapping identifier, in this case we used example. |
Also, "Example Inc" wants the admin console deployed on example.com/admin. To solve this, we will simply add another mapping to the config:
mapping.adm.host = example.com
mapping.adm.source = /admin
mapping.adm.target = /admin
mapping.adm.idProvider.system = default
|
This time we also added an ID provider to the mapping. This effectively activates |
Testing vhosts
To verify that your vhost config is working without setting up proxies or modifying your DNS: Simply add the following line to your hosts file.
<host-ip-address> example.com
Location of hosts file on Mac/Linux_: /etc/hosts, on Windows: c:\Windows\System32\Drivers\etc\hosts |
Pointing your browser to http://example.com:8080 will reveal the glorious result.
Visit the vhost configuration section for more details.