Enabling Cors In Geoserver With Jetty 9 Servlet Filter
What is CORS?
CORS stands for Cross-Origin Resource Sharing. It is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the resource originated. CORS defines a way in which a browser and server can interact to determine whether it is safe to allow a cross-origin request.
CORS enables servers to specify who can access the assets on the server, at the file and directory level. Using CORS, servers can define domains that can access asset directories and files. This ensures only specified domains can retrieve assets while blocking all other cross-origin requests.
Same-origin policy and need for CORS
Web browsers implement the same-origin policy to prevent malicious actors from reading sensitive data from another site. Under this policy, a web browser permits scripts in a web page to access data in another web page but only if both web pages have the same origin. An origin is defined by the domain, protocol, and port.
This policy blocks cross-origin HTTP requests initiated from scripts. For example, a script on Domain A cannot make AJAX requests to Domain B. This creates issues for web apps that need to access cross-origin data.
CORS provides a secure way to allow cross-origin requests without compromising security. Using CORS, servers can specify what cross-origin requests are permitted from web pages. CORS-enabled servers instruct browsers to permit specific cross-origin requests while rejecting all others.
How CORS works
CORS works through HTTP headers that allow servers and browsers to communicate about permitted requests:
- Origin – Indicates the origin domain sending the request
- Access-Control-Request-Method – Indicates HTTP method of CORS request
- Access-Control-Request-Headers – Custom headers client wants to send
- Access-Control-Allow-Origin – Specifies permitted origins
- Access-Control-Allow-Methods – Allowed HTTP methods for CORS
- Access-Control-Allow-Headers – Permitted headers for CORS request
- Access-Control-Max-Age – How long browser can cache CORS info
When a CORS request is made, the browser sends an Origin header indicating the origin domain. The server uses the Access-Control headers to communicate permitted origins, methods, headers, and caching to the client. If the server permits the request, the client lets the browser complete the CORS request.
Why Enable CORS in GeoServer
Enabling CORS in GeoServer removes restrictions on accessing GeoServer remotely from web applications. By default, GeoServer cannot be accessed from an application hosted on another domain due to same-origin policy.
Enable cross-origin app access to GeoServer
The main reason to enable CORS in GeoServer is to allow web applications on other domains to access GeoServer functionality. For example, enabling CORS allows a web mapping application hosted on Domain A to access GeoServer hosted on Domain B.
Access GeoServer APIs from client-side code
Enabling CORS allows the GeoServer REST API to be called from client-side JavaScript code. For example, JavaScript code on a web page can make AJAX requests to the /ows endpoint to get data from GeoServer layers.
Avoid preflight request limits
CORS requires the browser to make an OPTIONS request to check that the cross-origin request is permitted. If CORS is not enabled, GeoServer will not permit the preflight which prevents the actual CORS request. This results in confusing browser errors about CORS failures.
Configuring CORS in GeoServer
Configuring CORS support in GeoServer requires installing a CORS filter in the web container and updating the web.xml file. The open source Jetty servlet container provides a CORS filter that can be enabled.
Installing the CORS Filter
By default, GeoServer runs on top of a Jetty web container. Jetty provides a built-in servlet filter that implements CORS called CrossOriginFilter. To install this filter:
- Copy web_cors.jar file from Jetty distro to [GEOSERVER_HOME]/lib
- Copy javax.servlet-api-3.1.jar from Tomcat or other servlet container to [GEOSERVER_HOME]/lib if needed
- Remove any other javax.servlet*.jar files in [GEOSERVER_HOME]/lib
The CrossOriginFilter will now be available to GeoServer after restarting.
Configuring web.xml
The main GeoServer web application configuration file called web.xml needs to be updated to enable the CORS filter. The filter mapping needs to be added:
<filter> <filter-name>cross-origin</filter-name> <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> </filter> <filter-mapping> <filter-name>cross-origin</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
This mapping will enable CORS on all GeoServer endpoints allowing CORS headers to be returned in the responses.
Sample CORS Filter Configuration
Further configuration of permitted origins, headers, methods, etc. can be done through filter parameters:
<filter> <filter-name>cross-origin</filter-name> <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> <init-param> <param-name>allowedOrigins</param-name> <param-value>http://app.example.com</param-value> </init-param> <init-param> <param-name>allowedMethods</param-name> <param-value>GET,POST,OPTIONS</param-value> </init-param> </filter>
Refer to the Jetty CORS filter documentation for details on all the available parameters.
Testing the CORS Configuration
There are several methods to test that CORS has been configured properly in GeoServer:
- Browser developer tools – Check CORS headers are returned for GeoServer responses.
- cURL – Test preflight OPTIONS requests return 200 OK status.
- Simple JavaScript app – Have app loaded in browser try to access GeoServer API or OWS endpoints and check for errors.
If configured properly, there should be no CORS errors when accessing GeoServer cross-origin. The browser should allow JavaScript code, XHR requests, web worker threads, etc. to access GeoServer across origins.
Troubleshooting CORS Issues
Some common problems with CORS configurations in GeoServer include:
- Failure of preflight OPTIONS requests – The OPTIONS request is rejected so all CORS requests fail.
- “Origin header not allowed” browser errors – Likely the origin domain is not listed in the allowedOrigins filter param.
- No Access-Control-Allow-Origin header – Filter mapping may be incorrect or CORS headers are getting stripped.
- Custom headers blocked – May need to add headers to allowedHeaders parameter.
Tools like browser developer tools and curl are useful for debugging CORS issues:
- Check status codes and headers for preflight OPTIONS and actual CORS requests.
- Try permissive CORS filter settings (e.g. allow all origins/methods) to isolate the issue.
- Check GeoServer logs for any reported errors related to CORS filter.
CORS-related issues can be tricky for developers to debug so methodically verifying expected headers, status codes, and errors at every step is key.
Best Practices for CORS with GeoServer
When enabling CORS in GeoServer, it is best to follow security and performance best practices including:
- Restrict origins – Only allow required web app domains instead of open access (*).
- Limit headers – Only allow required headers like Origin/Accept instead of all (*).
- Set max age – Increase max age to allow more preflight caching.
- RateLimit – Use Jetty IPAccessHandler to rate limit if needed.
- Disable when possible – Turn off CORS filter if cross-origin access not required.
CORS opens up GeoServer to more remote access so taking care to restrict permissions and limit performance impact is important for production servers.