Operational Guide: CORS & CSP Configuration in Spatial IaC

Cross-Origin Resource Sharing (CORS) and Content Security Policy (CSP) serve as the primary browser-enforced security boundaries for modern geospatial platforms. When delivering vector tiles, raster imagery, or spatial analytics APIs, improperly configured HTTP headers directly degrade client-side rendering, increase data exfiltration vectors, and break cross-service interoperability. Within a Spatial Infrastructure as Code (IaC) practice, these directives must be managed as declarative, version-controlled assets integrated into the broader Network Security & Access Control framework. Manual console modifications are strictly prohibited in production environments due to the inherent risk of configuration drift and untracked state mutations.

GIS-Specific Header Constraints

Geospatial workloads operate under distinct networking constraints compared to conventional web applications. WebGL and canvas-based clients—including Mapbox GL JS, OpenLayers, and Cesium—issue high-frequency cross-origin requests to tile servers, WMS/WFS endpoints, and geocoding APIs. Browsers enforce strict same-origin policies that require explicit Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers headers, as defined in the W3C Cross-Origin Resource Sharing specification. Concurrently, CSP mitigates cross-site scripting (XSS) and data injection by constraining script, style, image, and frame sources per the MDN Content Security Policy reference.

In spatial architectures, these headers must synchronize with CDN edge caching rules, API gateway routing logic, and backend service boundaries. Production deployments must enforce strict domain allowlists aligned with registered SaaS tenants or agency portals, explicitly rejecting wildcard (*) origins to prevent unauthorized data harvesting.

When the policy is correct, the browser preflight resolves before the tile request proceeds. The exchange for an authenticated tile fetch is:

sequenceDiagram
  participant B as Browser
  participant E as CDN / API gateway
  participant O as Tile origin
  B->>E: OPTIONS preflight + Origin
  E-->>B: 204 + Access-Control-Allow-* headers
  B->>E: GET tile + Origin
  E->>O: forward on cache miss
  O-->>E: tile + Vary: Origin
  E-->>B: 200 tile, cached per origin

Declarative Implementation in Terraform & Pulumi

Centralizing header logic within parameterized infrastructure modules ensures environment parity across development, staging, and production. The only variables that should differ between environments are the allowed origin lists and CSP violation reporting endpoints.

Terraform Implementation:

locals {
  cors_headers = {
    "Access-Control-Allow-Origin"  = var.allowed_origins
    "Access-Control-Allow-Methods" = "GET, POST, OPTIONS"
    "Access-Control-Allow-Headers" = "Authorization, Content-Type, X-Requested-With"
    "Access-Control-Max-Age"       = "86400"
  }
  csp_directives = {
    "default-src" = "'self'"
    "connect-src" = join(" ", var.tile_endpoints)
    "img-src"     = join(" ", var.tile_endpoints)
    "script-src"  = "'self' https://cdn.jsdelivr.net"
    "report-uri"  = var.csp_report_endpoint
  }
}

resource "aws_cloudfront_response_headers_policy" "cors_csp" {
  name = "spatial-platform-cors-csp-${var.environment}"
  cors_config {
    access_control_allow_credentials = false
    access_control_allow_origins {
      items = var.allowed_origins
    }
    access_control_allow_methods {
      items = ["GET", "POST", "OPTIONS"]
    }
    access_control_allow_headers {
      items = ["Authorization", "Content-Type"]
    }
    origin_override = true
  }
  security_headers_config {
    content_security_policy {
      content_security_policy = join("; ", [for k, v in local.csp_directives : "${k} ${v}"])
      override                = true
    }
  }
}

resource "aws_cloudfront_distribution" "spatial_cdn" {
  # ... origin, restrictions, and viewer_certificate config ...
  default_cache_behavior {
    # ... target_origin_id, viewer_protocol_policy, allowed_methods ...
    response_headers_policy_id = aws_cloudfront_response_headers_policy.cors_csp.id
  }
}

Pulumi Implementation (TypeScript):

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const config = new pulumi.Config();
const allowedOrigins = config.requireObject<string[]>("allowedOrigins");
const tileEndpoints = config.requireObject<string[]>("tileEndpoints");

const cspPolicy = [
  `default-src 'self'`,
  `connect-src ${tileEndpoints.join(" ")}`,
  `img-src ${tileEndpoints.join(" ")}`,
  `script-src 'self' https://cdn.jsdelivr.net`,
  `report-uri ${config.require("cspReportEndpoint")}`
].join("; ");

const responseHeadersPolicy = new aws.cloudfront.ResponseHeadersPolicy("spatialCorsCsp", {
  name: `spatial-platform-cors-csp-${pulumi.getStack()}`,
  corsConfig: {
    accessControlAllowCredentials: false,
    accessControlAllowOrigins: { items: allowedOrigins },
    accessControlAllowMethods: { items: ["GET", "POST", "OPTIONS"] },
    accessControlAllowHeaders: { items: ["Authorization", "Content-Type"] },
    originOverride: true,
  },
  securityHeadersConfig: {
    contentSecurityPolicy: {
      contentSecurityPolicy: cspPolicy,
      override: true,
    },
  },
});

State management implications are critical here. Both tools track response header policies as immutable resources. Modifying CSP or CORS directives in-place triggers resource replacement, which can cause brief CDN cache invalidation windows. To mitigate service disruption, implement blue-green header deployments or utilize versioned policy names, allowing seamless cutover without invalidating edge caches. Enable remote state locking and schedule automated drift detection jobs to flag out-of-band console edits.

Pipeline Validation & Security Guardrails

Automated policy-as-code checks must intercept misconfigurations before they reach production. Pre-merge CI/CD pipelines should parse IaC templates using tools like checkov, tfsec, or pulumi-policy to enforce constraints such as:

  • Access-Control-Allow-Origin must not contain * in production stacks.
  • CSP default-src must explicitly exclude unsafe-inline and unsafe-eval.
  • report-uri or report-to must point to a monitored logging endpoint.

Embedding these checks directly into the deployment pipeline ensures that spatial header configurations remain compliant with organizational security baselines. For teams standardizing on WebGL-based rendering pipelines, Configuring CORS Headers for Mapbox GL JS via IaC provides a reference implementation for aligning connect-src and img-src directives with vector tile endpoints.

Integration with Core Spatial Infrastructure

CORS and CSP configurations do not operate in isolation. They must align with underlying network and identity controls to form a cohesive security posture.

  • Network Routing & Edge Boundaries: Header enforcement at the CDN or API Gateway level must complement subnet isolation and routing tables. Misaligned routing can cause preflight OPTIONS requests to bypass security layers or hit unintended backend services. Properly configuring VPC Routing for Tile Servers ensures that cross-origin requests traverse designated security groups and NAT gateways before reaching tile generation workers.
  • Identity & Access Mapping: When spatial APIs require authenticated tile access, CORS preflight requests must be handled without triggering credential validation failures. API gateways should route OPTIONS requests to a lightweight handler that returns headers immediately, while GET/POST requests validate tokens against IAM Role Mapping for GIS configurations. This separation prevents CORS failures from masking legitimate authentication errors.
  • Security Group & Audit Hardening: Restrict ingress rules on tile servers to only accept traffic from CDN edge IP ranges or API gateway endpoints. Combine this with centralized audit logging (e.g., AWS CloudTrail, Azure Activity Log, or GCP Audit Logs) to track CSP violation reports and CORS rejection metrics. Forward these logs to a SIEM for anomaly detection, enabling rapid response to unauthorized origin attempts or potential data scraping campaigns.

Operational Best Practices

  1. Version Control Headers: Treat CSP and CORS definitions as infrastructure code. Store them in shared modules with strict semantic versioning.
  2. CSP Reporting Mode: Deploy new CSP directives in Content-Security-Policy-Report-Only mode for 48–72 hours in staging to capture false positives before enforcing strict blocking.
  3. State Locking & Drift Detection: Enable remote state locking and schedule automated drift detection jobs. Header modifications made outside IaC will be flagged and reconciled on the next apply.
  4. Cache Invalidation Strategy: Coordinate header updates with CDN cache purges. Use cache-busting query parameters for tile requests during transition periods to prevent stale headers from serving to clients.

Adhering to these operational standards ensures that geospatial platforms maintain strict browser-level security controls while preserving high availability, cross-tenant isolation, and auditability across multi-cloud deployments.