Adobe Experience Manager’s Cross-Origin Resource Sharing (CORS) facilitates non-AEM web properties to make client-side calls to AEM, both authenticated and unauthenticated, to fetch content or directly interact with AEM.
CORS configurations are managed as OSGi configuration factories in AEM, with each policy being represented as one instance of the factory.
http://<host>:<port>/system/console/configMgr > Adobe Granite Cross Origin Resource Sharing Policy
Adobe Granite Cross-Origin Resource Sharing Policy (com.adobe.granite.cors.impl.CORSPolicyImpl
)
A policy is selected by comparing the
Allowed Origin
with the Origin
request headerAllowed Paths
with the request path.The first policy matching these values are used. If none is found, any CORS request is denied.
If no policy is configured at all, CORS requests will also not be answered as the handler is disabled and thus effectively denied - as long as no other module of the server responds to CORS.
"alloworigin" <origin> | *
origin
parameters specifying URIs that may access the resource. For requests without credentials, the server may specify * as a wildcard, thereby allowing any origin to access the resource. It is absolutely not recommended to use Allow-Origin: *
in production since it allows every foreign (i.e. attacker) website to make requests that without CORS are strictly prohibited by browsers."alloworiginregexp" <regexp>
regexp
regular expressions specifying URIs that may access the resource. Regular expressions can lead to unintended matches if not carefully built, allowing an attacker to use a custom domain name that would also match the policy. It is generally recommended to have separate policies for each specific origin hostname, using alloworigin
, even if that means repeated configuration of the other policy properties. Different origins tend to have different life-cycles and requirements, thus benefitting from clear separation."allowedpaths" <regexp>
regexp
regular expressions specifying resource paths for which the policy applies."exposedheaders" <header>
"maxage" <seconds>
seconds
parameter indicating how long the results of a pre-flight request can be cached."supportedheaders" <header>
header
parameters indicating which HTTP headers can be used when making the actual request."supportedmethods"
"supportscredentials" <boolean>
boolean
indicating whether or not the response to the request can be exposed to the browser. When used as part of a response to a pre-flight request, this indicates whether or not the actual request can be made using credentials.Site 1 is a basic, anonymously accessible, read-only scenario where content is consumed via GET requests:
{
"supportscredentials":false,
"exposedheaders":[
""
],
"supportedmethods":[
"GET",
"HEAD",
"OPTIONS"
],
"alloworigin":[
"http://127.0.0.1:3000",
"https://site1.com"
],
"maxage:Integer": 1800,
"alloworiginregexp":[
"http://localhost:.*"
"https://.*\.site1\.com"
],
"allowedpaths":[
"/content/_cq_graphql/site1/endpoint.json",
"/graphql/execute.json.*",
"/content/site1/.*"
],
"supportedheaders":[
"Origin",
"Accept",
"X-Requested-With",
"Content-Type",
"Access-Control-Request-Method",
"Access-Control-Request-Headers",
]
}
Site 2 is more complex and requires authorized and mutating (POST, PUT, DELETE) requests:
{
"supportscredentials":true,
"exposedheaders":[
""
],
"supportedmethods":[
"GET",
"HEAD"
"POST",
"DELETE",
"OPTIONS",
"PUT"
],
"alloworigin":[
"http://127.0.0.1:3000",
"https://site2.com"
],
"maxage:Integer": 1800,
"alloworiginregexp":[
"http://localhost:.*"
"https://.*\.site2\.com"
],
"allowedpaths":[
"/content/site2/.*",
"/libs/granite/csrf/token.json",
],
"supportedheaders":[
"Origin",
"Accept",
"X-Requested-With",
"Content-Type",
"Access-Control-Request-Method",
"Access-Control-Request-Headers",
"Authorization",
"CSRF-Token"
]
}
Starting with Dispatcher 4.1.1+ response headers can be cached. This makes it possible to cache CORS headers along w the CORS-requested resources, as long as the request is anonymous.
Generally, the same considerations for caching content at Dispatcher can be applied to caching CORS response headers at dispatcher. The following table defines when CORS headers (and thus CORS requests) can be cached.
Cacheable | Environment | Authentication Status | Explanation |
---|---|---|---|
No | AEM Publish | Authenticated | Dispatcher caching on AEM Author is limited to static, non-authored assets. This makes it difficult and impractical to cache most resources on AEM Author, including HTTP response headers. |
No | AEM Publish | Authenticated | Avoid caching CORS headers on authenticated requests. This aligns to the common guidance of not caching authenticated requests, as it is difficult to determine how the authentication/authorization status of the requesting user will effect the delivered resource. |
Yes | AEM Publish | Anonymous | Anonymous requests cache-able at dispatcher can have their response headers cached as well, ensuring future CORS requests can access the cached content. Any CORS configuration change on AEM Publish must be followed by an invalidation of affected cached resources. Best practices dictate on code or configuration deployments the dispatcher cache is purged, as it’s difficult to determine what cached content may be effected. |
To allow the caching of CORS headers, add the following configuration to all supporting AEM Publish dispatcher.any files.
/myfarm {
...
/headers {
"Origin"
"Access-Control-Allow-Origin"
"Access-Control-Expose-Headers"
"Access-Control-Max-Age"
"Access-Control-Allow-Credentials"
"Access-Control-Allow-Methods"
"Access-Control-Allow-Headers"
}
...
}
Remember to restart the web server application after making changes to the dispatcher.any
file.
It is likely clearing the cache entirely is required to ensure the headers are appropriately cached on the next request after a /cache/headers
configuration update.
Logging is available under com.adobe.granite.cors
:
DEBUG
to see details about why a CORS request was deniedTRACE
to see details about all requests going through the CORS handlerAccess-Control-Allow-Origin
header is absent on the response, review the logs for denials under DEBUG in com.adobe.granite.cors
/cache/headers
configuration is applied to dispatcher.any
and the web server is successfully restarted