Cross-Origin Resource Sharing
- Last UpdatedJun 15, 2023
- 7 minute read
- PI System
- PI Web API Reference
- Developer
This topic explains CORS configuration in PI Web API, as well as instructions for setting up CORS correctly.
Background
Cross-Origin Resource Sharing (CORS) is a World Wide Web Consortium (W3C) specification for secure access to resources hosted in a remote domain. This was introduced to overcome the same-origin policy restriction imposed by most modern web browsers. The same-origin policy limits scripts running in one domain from accessing resources that are hosted in another for security purposes. For example, your application that runs on http://my-internal-site.internal/mygreatapp will not be allowed to make AJAX calls to PI Web API running at https://my-pisystem.internal/piwebapi since https://my-internal-site.internal and https://my-pisystem.internal have different origins.
If you are writing an application against PI Web API with scripts running on the browser (e.g., making an AJAX call in your JavaScript application) and seeing error messages in the browser console related to cross-origin requests, you need to enable CORS in the PI Web API configuration. Otherwise, the browser will prevent your script from accessing resources provided by PI Web API.
With CORS set up, the browser sends the request to the PI Web API server and determines whether the request is allowed based on CORS settings in PI Web API. There are two different ways that the browser can ask for permissions. For simplicity:
- Simple cross-domain request: one HTTP request
- Complex cross-domain request: two HTTP requests (preflight request + actual request). The preflight request is made automatically by the browser and uses the OPTIONS method. The actual request is only sent if the permission check is successful for the preflight request.
For more information about simple vs. complex requests, and about CORS in general, consult the following resources:
- W3C recommendation of CORS
- Firefox implementation of CORS
- CORS Support in ASP.NET Web API 2
- The PI Web API User's Guide also contains a section on CORS.
Configuration Settings
By default, CORS is disabled in PI Web API. To enable CORS or change CORS settings, choose one of the following actions:
- Change the CORS related attributes in the AF configuration database under the element OSIsoft > PI Web API > (Your PI Web API Instance Name) > System Configuration.
- Use the PUT action in the Configuration controller.
The following CORS configuration items are available in PI Web API:
-
CorsOrigins (default: null) The CorsOrigins attribute contains a comma-separated list of domains from which CORS requests may originate. In particular:
- To indicate cross-domain requests cannot be accepted from any origin, use an empty string. (Effectively disables CORS.)
- To indicate cross-domain requests are acceptable from any origins, use the asterisk (*) character for wildcard. (Not recommended.)
Here is an example of a partial simple cross-domain request. The
Originheader gives the domain of the site that is making the request.GET http://my-pisystem.internal/piwebapi HTTP/1.1 Origin: http://my-internal-site.internalTo allow this request, the CorsOrigins attribute in your PI Web API configuration needs to be set to the wildcard value *, or contain
http://my-internal-site.internalin the list. If set correctly, the server will allow the request and set theAccess-Control-Allow-Originheader. The value of this header either matches theOriginheader, or is the wildcard value *. Here is a sample response:HTTP/1.1 200 OK Access-Control-Allow-Origin: http://my-internal-site.internalIf CorsOrigins attribute is not set correctly, the browser disallows the request and the response does not include the
Access-Control-Allow-Origin header.Tips:
- Check the
Originheader in the request,and make sure the value matches with the header specified in the CorsOrigins attribute. - Make sure the origin is in correct URL format (e.g., "my-internal-site" is not a valid URL).
- Do not include a space between the comma-separated list of origins.
- Do not include a forward slash at the end of the origins URL.
- Example configuration:
http://my-internal-site.internal,http://my-internal-site2.internal:8080
-
CorsMethods (default: GET,OPTIONS)The CorsMethods attribute contains a comma-separated list of HTTP methods (verbs) that are allowed in cross-domain requests. In particular:
- To indicate cross-domain requests cannot be accepted with any HTTP method, use an empty string. (Effectively disables CORS.)
- To indicate that cross-domain requests are acceptable using any HTTP method, use the asterisk (*) character for wildcard.
This is only used in a preflight request. Here is an example of a partial preflight cross-domain request.
OPTIONS http://my-pisystem.internal/piwebapi/elements/I1EmDqD5loBNH0erqeqJodtALAaqQoQHk26BGgMQAVXYR0Ag HTTP/1.1 Origin: http://my-internal-site.internal Access-Control-Request-Method: PUT Access-Control-Request-Headers: authorization,content-typeNote that it is an OPTION request (preflight). To allow this request, the CorsMethod attribute in your PI Web API configuration needs to be set to the wildcard value *, or contain
PUTin the comma-separated list. (CorsHeader will be discussed in the next section.) If set correctly, the server will allow the request and set theAccess-Control-Allow-Methodheader. Here is a sample response:HTTP/1.1 200 OK Access-Control-Allow-Origin: http://my-internal-site.internal Access-Control-Allow-Methods: PUT Access-Control-Allow-Headers: authorization,content-typeFollowing the preflight request, you will see the actual PUT request and response from the server, similar to the request and response you saw in the simple CORS request in the CorsOrigins section.
Tips:
- Check the
Access-Control-Request-Methodrequest header, and make sure the value matches with the method specified in the CorsMethods attribute. - Method names need to be in all upper case.
- Do not include a space between the comma-separated list of methods.
- Example configuration:
GET,OPTIONS,POST
-
CorsHeaders (default: null) The CorsHeaders attribute contains a comma-separated list of HTTP header keys that are allowed for cross-domain requests. In particular:
- To indicate cross-domain requests cannot be accepted with any HTTP headers, use a null or empty string.
- To indicate that cross-domain requests can include any HTTP headers, use the asterisk (*) character for wildcard.
Similar to CorsMethods, this is only used in the preflight request. Look at the sample preflight request in the CorsMethods section. To allow this request, the CorsHeader attribute in your PI Web API configuration needs to be set to the wildcard value *, or contain
authorizationandcontent-typein the comma-separated list. If set correctly, the server will allow the request and set theAccess-Control-Allow-Headersheader, as shown in the sample response above.Tips:
- Check the
Access-Control-Request-Headersvalue in the request, and make sure it matches with the headers specified in the CorsHeaders attribute. - Do not include a space between the comma-separated list of headers.
- If you set CorsHeaders to anything other than
\*, you should include at least addaccept,content-type, andorigin, plus any custom headers that you want to support. This is because browsers are not consistent in how they setAccess-Control-Request-Headers. - Example configuration:
accept,authorization,content-type,origin
-
CorsExposedHeaders (default: Allow,Content-Encoding,Content-Length,Date,Location)The CorsExposedHeaders attribute contains a comma-separated list of HTTP response headers that browsers are allowed to access in addition to the simple response headers exposed by the browser by default. The response headers that are available by default are:
Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma
To make other headers available to the application, include them in the list of CorsExposedHeaders attribute. For example, if you are creating an element using CreateElement and would like to get the url resource for the newly created element from the
Locationheader, theLocationheader needs to be explicitly specified in CorsExposedHeaders because it is not exposed by default.Tips:
- Do not confuse CorsExposedHeaders with CorsHeaders. The CorsHeaders attribute is used to specify the HTTP headers that are allowed in the requests, while the CorsExposedHeaders attribute is used to specify the HTTP headers that are exposed in the responses.
- Do not include a space between the comma-separated list of headers.
- Example configuration:
Allow,Content-Encoding,Content-Length,Date,Location
-
CorsSupportsCredentials (default: false)The CorsSupportsCredentials attribute indicates whether the browser can send any user credentials with cross-domain requests.
- False: authentication information sent from the browser is not allowed in the request.
- True: authentication information sent from the browser is allowed in the request.
An example response when the CorsSupportsCrendentials is set to true:
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://my-internal-site.internal Access-Control-Allow-Methods: PUT Access-Control-Allow-Headers: authorization,content-type Access-Control-Allow-Credentials: trueTips:
- CorsSupportsCredentials cannot be used in conjunction with a wildcard (*) value for CorsOrigins.
- Set this flag to
trueif you are using Basic or Kerberos Authentication. - Example configuration:
true
-
PreflightMaxAge (default: -1)PI Web API can indicate to client applications how long to cache the preflight request. Set to -1 to never send an Access-Control-Max-Age header in the preflight response. Set to 0 to instruct the client to never cache the preflight response.
Troubleshooting Tips
If you are troubleshooting CORS errors, the browser developer tools (usually opened by pressing F12) provide many useful ways to diagnose the error. Remember that the same-origin policy is a browser implementation, so different browsers may exhibit slightly different behaviors.
-
Look at the error messages in the console. For example,
- Chrome:
XMLHttpRequest cannot load https://my-internal-site.internal/piwebapi. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://my-internal-site.internal' is therefore not allowed access. The response had HTTP status code 400. - Firefox:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https:// my-internal-site.internal/piwebapi. (Reason: CORS header 'Access-Control-Allow-Origin' missing). - Internet Explorer:
XMLHttpRequest: Network Error 0x80070005, Access is denied.
- Chrome:
-
The browser usually does not give us details on why the error occurs. Even though the browser specifically says that the CORS header
Access-Control-Allow-Originis missing, this does not mean that the CorsOrigins attribute is the culprit. This is because the server returns a generic response without any CORS headers if any of the CORS check fails. -
To identify the misconfigured CORS setting, inspect the request headers.
- Make sure the value in the
Originheader in the request matches with one of the origins specified in the CorsOrigins attribute (unless a wildcard is used). - Make sure the value in the
Access-Control-Request-Methodin the preflight request matches with one of the methods specified in the CorsMethods attribute (unless a wildcard is used). - Make sure the values in the
Access-Control-Request-Headersin the preflight request match the headers specified in the CorsHeaders attribute (unless a wildcard is used). - If your client application is requesting for a specific header (e.g.,
Location), make sure it is included in the CorsExposedHeaders list. - Make sure CorsSupportsCredentials is
trueif you are using Basic or Kerberos authentication.
- Make sure the value in the
-
Changing configuration setting does not require a restart of the PI Web API service since the service will update its configuration every 15 seconds or so. To make sure the new configuration values are accepted, check the admin logs in the event viewer for any errors and to verify the modified configuration values.