OpenLink Virtuoso is a hybrid database that combines relational, graph, and document data management. It's commonly used for RDF data, SPARQL endpoints, and Linked Open Data, which are frequently accessed cross-origin. Proper CORS configuration is critical for these use cases.
Access-Control-Allow-Origin: * allows any website to access your resources. Always specify exact origins in production.
GUI configuration requires:
For older versions, use the VSP code approach shown below.
For basic CORS setup using the Virtuoso Conductor:
https://example.com or https://app.example.com. Do not use wildcard * in production.Note: GUI configuration is more efficient than VSP code as it processes CORS at the server level before application code executes.
For programmatic control or older Virtuoso versions, implement CORS in VSP (Virtuoso Server Pages) using http_request_header() and http_header() functions.
<?vsp
-- Enhanced Virtuoso CORS implementation
DECLARE allowed_origins ANY;
DECLARE request_origin VARCHAR;
DECLARE request_method VARCHAR;
DECLARE i INT;
-- List of allowed origins
allowed_origins := vector(
'https://example.com',
'https://app.example.com',
'https://dashboard.example.com'
);
-- Get request origin and method
request_origin := http_request_header(lines, 'Origin', NULL);
request_method := http_request_header(lines, 'REQUEST_METHOD', NULL);
-- Validate origin against whitelist
FOR (i := 0; i < length(allowed_origins); i := i + 1)
{
IF (request_origin = allowed_origins[i])
{
-- Origin is valid, set CORS headers
http_header(sprintf('Access-Control-Allow-Origin: %s\r\n', request_origin));
http_header('Vary: Origin\r\n');
GOTO origin_valid;
}
}
-- Origin not valid - reject CORS
RETURN;
origin_valid:
-- Handle preflight OPTIONS request
IF (request_method = 'OPTIONS')
{
http_header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS\r\n');
http_header('Access-Control-Allow-Headers: Content-Type, Authorization, Accept\r\n');
http_header('Access-Control-Max-Age: 86400\r\n');
http_status_set(204);
RETURN;
}
-- Your application logic here
http_header('Content-Type: application/json\r\n');
http('{"message": "CORS enabled"}');
?>
To allow all subdomains of a domain using pattern matching:
<?vsp
DECLARE request_origin VARCHAR;
DECLARE origin_pattern VARCHAR;
request_origin := http_request_header(lines, 'Origin', NULL);
-- Allow all subdomains of example.com
origin_pattern := '^https?://([a-zA-Z0-9-]+\\.)?example\\.com$';
IF (regexp_match(origin_pattern, request_origin) IS NOT NULL)
{
http_header(sprintf('Access-Control-Allow-Origin: %s\r\n', request_origin));
http_header('Vary: Origin\r\n');
}
ELSE
{
-- Origin not allowed
RETURN;
}
-- Continue with application logic...
?>
Performance Note: Pattern matching with regex runs on every request. For better performance, use explicit origin lists or GUI configuration when possible.
SPARQL endpoints are frequently accessed cross-origin and require special consideration:
<?vsp
-- CORS for SPARQL endpoint
DECLARE request_origin VARCHAR;
DECLARE allowed_origins ANY;
request_origin := http_request_header(lines, 'Origin', NULL);
allowed_origins := vector(
'https://example.com',
'https://sparql-client.example.com'
);
-- Validate origin
IF (position(request_origin, allowed_origins) > 0)
{
http_header(sprintf('Access-Control-Allow-Origin: %s\r\n', request_origin));
http_header('Vary: Origin\r\n');
-- Handle preflight
IF (http_request_header(lines, 'REQUEST_METHOD', NULL) = 'OPTIONS')
{
http_header('Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n');
http_header('Access-Control-Allow-Headers: Content-Type, Accept\r\n');
http_header('Access-Control-Max-Age: 3600\r\n');
http_status_set(204);
RETURN;
}
}
-- Process SPARQL query...
?>
When using HTTP authentication, you must use a specific origin (not wildcard):
<?vsp
DECLARE request_origin VARCHAR;
request_origin := http_request_header(lines, 'Origin', NULL);
-- Must use specific origin, not wildcard
IF (request_origin = 'https://example.com')
{
http_header(sprintf('Access-Control-Allow-Origin: %s\r\n', request_origin));
http_header('Access-Control-Allow-Credentials: true\r\n');
http_header('Vary: Origin\r\n');
}
-- Application logic...
?>
For applications with multiple endpoints, create a reusable CORS procedure:
<?vsp
-- Reusable CORS function
CREATE PROCEDURE check_cors(
IN request_origin VARCHAR,
IN allowed_origins ANY)
{
DECLARE i INT;
FOR (i := 0; i < length(allowed_origins); i := i + 1)
{
IF (request_origin = allowed_origins[i])
{
http_header(sprintf('Access-Control-Allow-Origin: %s\r\n', request_origin));
http_header('Vary: Origin\r\n');
RETURN 1;
}
}
RETURN 0;
}
-- Usage in VSP pages
DECLARE origins ANY;
DECLARE request_origin VARCHAR;
origins := vector('https://example.com', 'https://app.example.com');
request_origin := http_request_header(lines, 'Origin', NULL);
IF (check_cors(request_origin, origins) = 0)
{
http_status_set(403);
http('{"error": "Origin not allowed"}');
RETURN;
}
-- Continue with application logic...
?>
For comprehensive testing instructions including curl commands, browser DevTools usage, and troubleshooting common CORS errors, see the CORS Testing Guide.
The content on this site stays fresh thanks to help from users like you! If you have suggestions or would like to contribute, fork us on GitHub.
Save 39% on CORS in Action with promotional code hossainco at manning.com/hossain