This article provides a complete, production-ready set of ColdFusion CORS (Cross-Origin Resource Sharing) policy examples that you can Download or copy into your projects. The resource focuses on secure, configurable headers for preflight handling, credentialed requests, and reverse-proxy deployments, helping you solve “blocked by CORS policy” errors quickly and safely.
Overview
CORS governs whether a browser is allowed to call your ColdFusion endpoints from a different origin (domain, subdomain, or port). When not configured correctly, AJAX/Fetch calls to your CFML REST APIs, JSON endpoints, or static files can fail with opaque errors.
This resource gives you:
- A tested, extensible Application.cfc implementation for Adobe ColdFusion and Lucee.
- Web server and container-level examples (Tomcat, IIS, Nginx, Apache) when you prefer to emit headers before requests reach ColdFusion.
- Ready-to-run tests with curl and browser Fetch, plus Best practices that reduce risk while improving developer productivity.
Use these examples to implement Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Credentials, Access-Control-Max-Age, Vary, and reliable handling of OPTIONS preflight requests.
What You’ll Get
– CFML application-level CORS helper (Application.cfc, CORS.cfc, and a lightweight include).
– Server and proxy samples (Tomcat web.xml filter, IIS CORS module config, Nginx and Apache header rules).
– Test assets (curl command set and a minimal browser Fetch page).
– Documentation notes and Best practices.
File contents overview:
- cfml/Application.cfc — Request lifecycle hooks and CORS functions
- cfml/CORS.cfc — reusable component with origin checks and header setters
- cfml/cors-include.cfm — drop-in include for legacy apps without Application.cfc edits
- server/tomcat/web.xml — Apache Tomcat CorsFilter snippet
- server/iis/web.config — IIS CORS Module example
- server/nginx.conf — proxy/header rules with origin whitelisting
- server/httpd.conf — Apache mod_headers + SetEnvIf example
- test/index.html — small page to test fetch() cross-origin calls
- test/curl-commands.txt — copy-paste curl tests for preflight and simple requests
- docs/README.txt — Quick reference and notes
Supported Environments
– Adobe ColdFusion 2018, 2021, 2023
– Lucee 5.3+ and Lucee 6+
– Tomcat 8.5, 9, 10
– IIS 10+ with the IIS CORS Module
– Nginx 1.18+
– Apache HTTP Server 2.4+
Quick Start
Option A — App-level CORS in CFML (Application.cfc)
Paste the following Application.cfc into the root of your app or merge the relevant parts into your existing file. It safely reflects allowed origins, handles OPTIONS preflight, and sets headers for actual requests. Works for APIs and traditional pages.
Note: This uses script Syntax for cfheader and runs on Adobe CF and Lucee.
cfml
component {
this.name = “CORSExampleApp”;
this.sessionManagement = false;
// Toggle credential support (cookies, Authorization headers, etc.)
variables.allowCredentials = true;
// Allowed origins. Supports exact matches and simple wildcard subdomains.
variables.allowedOriginPatterns = [
“https://example.com“,
“https://*.example.org“,
“http://localhost:3000“,
“http://127.0.0.1:8080”
];
// Allow/Expose/MaxAge choices
variables.allowedMethods = “GET,POST,PUT,PATCH,DELETE,OPTIONS”;
variables.allowedHeaders = “Authorization,Content-Type,X-Requested-With”;
variables.exposedHeaders = “X-Request-Id”;
variables.maxAgeSeconds = 3600;
function onRequestStart(targetPage){
var req = getHttpRequestData();
var method = uCase( cgi.request_method ?: “” );
var origin = req.headers[“Origin”] ?: “”;
if ( method eq "OPTIONS" && isCORSPreflight(req) ) {
// Preflight: respond without running the target page
addCORSHeaders(origin, req);
// 204 No Content is typical for preflight success
cfheader(statuscode=204, statustext="No Content");
// Required by Lucee/ACF to stop processing further
abort;
}
// For non-OPTIONS requests, add CORS headers (if allowed)
addCORSHeaders(origin, req);
return true;
}
function onRequest(targetPage) {
include arguments.targetPage;
}
function onError(exception, eventName){
// Ensure CORS headers still emitted on error responses
var req = getHttpRequestData();
addCORSHeaders(req.headers[“Origin”] ?: “”, req);
}
private boolean function isCORSPreflight(req){
return structKeyExists(req.headers, “Access-Control-Request-Method”);
}
private boolean function isAllowedOrigin(origin){
if (!len(origin)) return false;
// Simple matcher for exact and wildcard subdomain patterns.
for (var pattern in variables.allowedOriginPatterns) {
if (findNoCase("*.", pattern)) {
var base = rereplace(pattern, "^\s*https?://\*\.", "", "all");
var re = "^https?://([A-Za-z0-9-]+\.)*#REReplace(escapeRE(base),'\.','\.', 'all')#(?::\d+)?$";
if (reFindNoCase(re, origin)) return true;
} else {
if (compareNoCase(pattern, origin) == 0) return true;
}
}
return false;
}
private void function addCORSHeaders(origin, req){
// Always vary on Origin for caches/proxies
cfheader(name=”Vary”, value=”Origin”);
if (!isAllowedOrigin(origin)) {
// Optionally lock down non-matching origins by not setting CORS headers
return;
}
// With credentials, you must echo the specific origin (no "*")
cfheader(name="Access-Control-Allow-Origin", value=origin);
if (variables.allowCredentials) {
cfheader(name="Access-Control-Allow-Credentials", value="true");
}
// Preflight extras
if (structKeyExists(req.headers, "Access-Control-Request-Method")) {
cfheader(name="Access-Control-Allow-Methods", value=variables.allowedMethods);
// Reflect requested headers or apply a controlled allow-list
var requested = req.headers["Access-Control-Request-Headers"] ?: "";
if (len(requested)) {
// Normalize commas/spaces and lower-case for consistency
cfheader(name="Access-Control-Allow-Headers", value= lcase(reReplace(requested, "\s+", "", "all")) );
} else {
cfheader(name="Access-Control-Allow-Headers", value=variables.allowedHeaders);
}
cfheader(name="Access-Control-Max-Age", value= variables.maxAgeSeconds );
// Help caches vary per requested headers too
if (len(requested)) cfheader(name="Vary", value="Access-Control-Request-Headers");
} else {
// Actual requests can expose additional headers
if (len(variables.exposedHeaders)) {
cfheader(name="Access-Control-Expose-Headers", value=variables.exposedHeaders);
}
}
}
// Utility for regex escaping of host fragments
private string function escapeRE(str){
return rereplace(str, “([.\+*\?[\^]\$(){}\=!\<>|\:-])”, “\\1”, “all”);
}
}
If you cannot modify Application.cfc, include the header logic at the top of each API template using the provided cors-include.cfm.
Option B — Web server / container CORS headers (before ColdFusion)
Useful when serving mixed assets via a Reverse proxy, or when you prefer centralized governance.
-
Tomcat web.xml (CorsFilter)
xml
CorsFilter
org.apache.catalina.filters.CorsFilter
cors.allowed.origins https://example.com,http://localhost:3000
cors.allowed.methods GET,POST,PUT,PATCH,DELETE,OPTIONS
cors.allowed.headers Authorization,Content-Type,X-Requested-With
cors.exposed.headers X-Request-Id
cors.support.credentials true
cors.preflight.maxage 3600
CorsFilter
/*
-
IIS (IIS CORS Module)
xml
GET,POST,PUT,PATCH,DELETE,OPTIONS
X-Request-Id
3600
GET,POST,PUT,PATCH,DELETE,OPTIONS
-
Nginx (Reverse proxy)
nginx
map $http_origin $cors_allow {
default “”;
~^https?://(.*.example.org|example.com|localhost(:\d+)?|127.0.0.1(:\d+)?)$ $http_origin;
}
server {
…
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_allow always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Methods “GET,POST,PUT,PATCH,DELETE,OPTIONS” always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Access-Control-Max-Age 3600 always;
add_header Vary “Origin, Access-Control-Request-Headers” always;
return 204;
}
add_header Access-Control-Allow-Origin $cors_allow always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Expose-Headers “X-Request-Id” always;
location / {
proxy_pass http://cfml_upstream;
}
}
- Apache HTTPD (mod_headers + SetEnvIf)
apache
SetEnvIf Origin “^https?://(.*.example.org|example.com|localhost(:[0-9]+)?|127.0.0.1(:[0-9]+)?)$” ORIGIN_OK=1
Header always set Access-Control-Allow-Origin “%{Origin}e” env=ORIGIN_OK
Header always set Access-Control-Allow-Credentials “true” env=ORIGIN_OK
Header always set Access-Control-Expose-Headers “X-Request-Id” env=ORIGIN_OK
Header always set Vary “Origin” env=ORIGIN_OK
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
Header always set Access-Control-Allow-Methods “GET,POST,PUT,PATCH,DELETE,OPTIONS” env=ORIGIN_OK
Header always set Access-Control-Allow-Headers “%{Access-Control-Request-Headers}e” env=ORIGIN_OK
Header always set Access-Control-Max-Age “3600” env=ORIGIN_OK
How to Configure
Step-by-Step
1) Decide where to implement CORS
– App-level (CFML): fine-grained control and dynamic per-route logic.
– Edge/server-level: one place to manage static assets and APIs together, often faster and simpler at scale.
- Install the files
- App-level: Copy Application.cfc (or merge its functions) and CORS.cfc into your app root. Optionally use cors-include.cfm for legacy pages.
- Server-level: Apply the appropriate snippet to Tomcat, IIS, Nginx, or Apache, then reload/restart.
- Configure allowed origins
- Update allowedOriginPatterns (App-level) or lists in server configs. Prefer exact domains. Use wildcard subdomains only when necessary.
- If you need credentials (cookies or Authorization headers), do not use “*”. Echo the requesting origin.
- Set methods, headers, and max age
- Keep Access-Control-Allow-Headers to a minimal allow-list, or reflect the requested headers if you trust the client.
- Access-Control-Max-Age reduces preflight noise (e.g., 3600 seconds).
- Test
- Use the supplied curl commands and the test/index.html file to validate simple and preflight requests.
- Verify browser DevTools > Network shows the expected CORS headers on both OPTIONS and the actual request.
- Deploy and monitor
- In proxies or CDNs, ensure Vary: Origin is preserved to prevent cache poisoning.
- Log or expose a X-Request-Id to correlate Server logs with client requests.
Best Practices
– Prefer a whitelist of exact origins; only use wildcard subdomains when you fully own the parent domain.
– Set Vary: Origin and, for preflight, add Vary: Access-Control-Request-Headers.
– If Access-Control-Allow-Credentials: true, never set Access-Control-Allow-Origin: *.
– Do not rely on CORS for Authentication or authorization; it’s a browser policy, not an app-level Security control.
– Limit Access-Control-Allow-Headers to necessary headers (e.g., Authorization, Content-Type).
– Cache preflight responses via Access-Control-Max-Age to reduce latency and server load.
– Ensure error responses and 4xx/5xx also include CORS headers, so the browser doesn’t mask useful information.
– For frameworks and route-specific rules, inject header logic at a central middleware or onRequestStart.
Benefits and Use Cases
– Faster Integration: Drop-in CFML code prevents trial-and-error on header Syntax.
– Works across stacks: Choose app-level or server-level depending on your Deployment and Architecture.
– Lower latency: Preflight caching via Access-Control-Max-Age reduces round trips.
– Security-first defaults: Whitelists and Vary headers minimize cache and reflection risks.
– Common scenarios:
– Single-page apps calling ColdFusion REST endpoints from a different domain.
– Hybrid stacks where Nginx/Apache front a Tomcat-based ColdFusion engine.
– Mobile apps and Microservices using token-based auth (Authorization headers) with strict CORS.
Troubleshooting and Testing
– Quick curl tests:
bash
# Preflight (OPTIONS)
curl -i -X OPTIONS https://api.yourdomain.com/v1/orders \
-H “Origin: http://localhost:3000” \
-H “Access-Control-Request-Method: POST” \
-H “Access-Control-Request-Headers: Authorization, Content-Type”
Actual request
curl -i -X POST https://api.yourdomain.com/v1/orders \
-H “Origin: http://localhost:3000” \
-H “Authorization: Bearer TEST” \
-H “Content-Type: application/json” \
-d ‘{“test”:true}’
- Browser test page snippet (test/index.html):
<!doctype html>
- Common pitfalls:
- Missing CORS headers on error paths (ensure onError handlers add them).
- Wildcard “*” used with credentials (blocked by browsers).
- Reverse proxies stripping Vary or not forwarding Origin.
- Preflight allowed headers/methods not aligned with actual calls.
Key Takeaways
– Implement CORS where it fits your Architecture: either in CFML with Application.cfc or at the web server/proxy layer.
– Use exact origin whitelists and echo the origin if you need credentials.
– Always emit Vary: Origin and consider Access-Control-Max-Age to improve Performance.
– Test both preflight (OPTIONS) and actual requests; verify headers in browser DevTools.
FAQ
No. If Access-Control-Allow-Credentials is true, browsers require a specific origin in Access-Control-Allow-Origin. Use an allow-list and echo the requesting origin when it matches.
Do all requests trigger a preflight?
No. “Simple” requests (GET/HEAD/POST with simple headers and certain content types) may skip preflight. Any custom headers (e.g., Authorization) or methods like PUT/DELETE typically trigger a preflight OPTIONS request.
How do I apply different CORS rules per route?
At the CFML layer, check CGI.SCRIPT_NAME or route information inside onRequestStart and branch your allowedOrigins/methods accordingly. At the proxy/server layer, use location blocks (Nginx), Directory/Location sections (Apache), or separate filter mappings (Tomcat) for fine-grained control.
Why do I need Vary: Origin?
Without Vary: Origin, caches and CDNs can serve a response generated for one origin to another, causing broken behavior or security leaks. Vary instructs caches to keep separate variants per origin.
What’s the difference between CORS and CSP?
CORS regulates cross-origin network requests and responses in the browser. CSP (Content Security Policy) governs which sources can load scripts, styles, images, etc. They complement each other but solve different problems.
Download and Next Steps
– Copy the code examples above into the indicated files and adapt the origin lists, methods, and headers to your environment.
– If you need a zip bundle of these examples, combine the snippets under a folder structure matching the “What You’ll Get” section.
– Tip: Externalize origin lists via environment variables (getSystemEnvironment()) to avoid hardcoding per environment (dev/stage/prod).
