Geoserver And Jetty: Solving The Cors Issues
Understanding CORS Issues in GeoServer
GeoServer is a popular open source mapping server that allows publishing and editing geospatial data. It is built on top of Java and is commonly deployed in servlet containers like Tomcat or Jetty.
A common issue that arises when using GeoServer is CORS or Cross-Origin Resource Sharing errors. These errors occur when client-side code served from one origin tries to access the GeoServer API hosted on a different origin.
For example, a GeoServer instance may be hosted at http://geoserver.example.com while a web map is served from http://maps.example.com. When the map tries to access the WMS or WFS services on GeoServer, the browser blocks the requests due to CORS.
The CORS errors manifest as cryptic messages in the browser console like:
Access to XMLHttpRequest at 'http://geoserver.example.com/wms?...' from origin 'http://maps.example.com' has been blocked by CORS policy
This prevents the map from accessing the spatial data it needs to function. Solving these CORS issues is crucial for building geospatial web apps with GeoServer.
Same Origin Policy and CORS Basics
The CORS errors originate from the browser’s Same Origin Policy, a security measure that prevents scripts on one origin from accessing resources on another. An “origin” comprises the protocol, domain, and port.
Two URLs have the same origin if they have the same protocol, domain, and port. So http://example.com/map and http://example.com/data have the same origin, while http://app.example.com and https://example.com do not.
The Same Origin Policy prevents malicious scripts on one page from obtaining unauthorized access to sensitive data on another. But it also blocks legitimate cross-origin requests needed for modern web apps.
CORS provides a secure way to make cross-origin requests. The server can whitelist origins that are allowed to access its resources using HTTP headers like Access-Control-Allow-Origin
.
Common CORS Issues with GeoServer
Some common CORS issues that arise with GeoServer include:
- Accessing GeoServer WMS or WFS from a different origin like a web map
- Making AJAX requests to the GeoServer REST API
- Using the GeoWebCache tile services from a different origin
- Hosting GeoServer behind a reverse proxy on a slightly different origin
Solving the CORS issues allows building rich web map apps that can leverage the powerful geospatial capabilities of GeoServer.
Configuring Jetty for CORS
Jetty is a popular embeddable servlet container that GeoServer can be deployed on. It has native support for CORS filters that handle the Access-Control headers automatically.
Here is how to configure Jetty to solve CORS issues when running GeoServer.
Adding CORS Filter Mappings
Jetty provides a CrossOriginFilter
that can be enabled to handle CORS. This filter needs to be mapped to the appropriate URL patterns in the web app.
Here is an example webdefault.xml fragment showing how the filter can be mapped for GeoServer:
<filter> <filter-name>cross-origin</filter-name> <filter-class>org.eclipse.jetty.cors.CrossOriginFilter</filter-class> </filter> <filter-mapping> <filter-name>cross-origin</filter-name> <url-pattern>/wms/*</url-pattern> <url-pattern>/wfs/*</url-pattern> <url-pattern>/wcs/*</url-pattern> <url-pattern>/wps/*</url-pattern> <url-pattern>/ows/*</url-pattern> <url-pattern>/rest/*</url-pattern> <url-pattern>/gwc/rest/*</url-pattern> </filter-mapping>
This maps the filter to all the core GeoServer endpoint patterns that clients need CORS access for.
Configuring Allowed Origins
By default the CrossOriginFilter blocks all cross-origin requests. The allowed origins need to be explicitly whitelisted using init parameters.
Here is an example whitelisting two origins, the web map domain and a reverse proxy:
<init-param> <param-name>allowedOrigins</param-name> <param-value>http://maps.example.com|http://proxy.example.com</param-value> </init-param>
The wildcard character (*) can be used to allow access from all origins.
Other whitelist patterns like domain suffixes and regular expressions are also supported.
With that cross-origin requests from the allowed origins will get the appropriate CORS headers like Access-Control-Allow-Origin added automatically.
Example Configuration Code
Here is a full CORS filter configuration example for a GeoServer instance hosted at http://geo.domain.com:
<!-- CORS Filter --> <filter> <filter-name>cross-origin</filter-name> <filter-class>org.eclipse.jetty.cors.CrossOriginFilter</filter-class> <init-param> <param-name>allowedOrigins</param-name> <param-value>http://maps.domain.com|https://apps.domain.com</param-value> </init-param> <init-param> <param-name>allowedMethods</param-name> <param-value>GET,POST,HEAD</param-value> </init-param> <init-param> <param-name>allowedHeaders</param-name> <param-value>X-Requested-With,Content-Type,Accept,Origin</param-value> </init-param> </filter> <filter-mapping> <filter-name>cross-origin</filter-name> <url-pattern>/wms/*</url-pattern> <url-pattern>/wfs/*</url-pattern> <url-pattern>/rest/*</url-pattern> </filter-mapping>
This whitelists the web map and app origins and handles CORS headers for the WMS, WFS, and REST API endpoints accessed by client-side code.
Testing and Debugging CORS
Here are some tips for testing and debugging CORS issues:
- Use browser dev tools to inspect CORS headers and error messages
- Try requests from code snippet tools like JS Bin to isolate domains
- Test with a clean browser session and disable caches
- Log the CORS headers on the server for debugging
- Granularly map filters to find problem URLs
- Use separate test domains for client vs server side
- Cleanup whitelist params to find misconfigurations
Be meticulous in testing different use cases like async requests, preflight OPTIONS checks, redirects, etc.
Use tools like curl to directly test APIs without CORS checks.
Automated testing frameworks can also help catch CORS regressions during development.
Common CORS Pitfalls to Avoid
Some common pitfalls when dealing with CORS issues include:
- Assuming CORS is enabled by default
- Not having error handling for failed CORS requests
- Whitlisting localhost instead of actual domain names
- Exposing internal hosts publicly in whitelist configs
- Forgetting subdomains, TLDs, or protocols in the origin checks
- Mixed content issues with HTTPS vs HTTP
- Assuming CORS configured on one endpoint means entire server supports it
- Confusing concepts like CORS vs JSONP vs iframe proxying
Be extremely methodical in defining and testing exact whitelist origins and URL patterns.
Log and handle CORS errors appropriately on clients. Have fallback mechanisms and degradation support when CORS fails.
Regularly review and scrub CORS configs to avoid cruft build up allowing over-broad access.
Securing GeoServer with CORS
When configured correctly, CORS enhances the security of GeoServer APIs and OGC services:
- Whitelist client origins to prevent unauthorized access
- Require authentication for sensitive operations
- Limit HTTP methods to prevent unsafe requests
- Set expiration times for CORS whitelisting
Audit all CrossOriginFilter params and ensure:
- Only minimal required origins are whitelisted
- Subdomain wildcards are avoided if possible
- Origin patterns are specific to avoid regex DoS risks
Use IP and user agent filtering in addition to origin checks for more security.
Enable CORS on only minimal essential endpoints and URLs.
These precautions help secure GeoServer by preventing unwanted cross-origin access to geospatial resources.