When implementing Cross-Origin Resource Sharing (CORS), browsers automatically send OPTIONS requests as "preflight" checks before making actual requests. While we've set up the necessary headers:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
The server still needs to properly handle these OPTIONS requests without executing application logic.
Using mod_rewrite for this purpose causes issues:
# This approach loses CORS headers
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
The headers disappear because rewrite rules process before header modifications in Apache's request cycle.
Here are three robust approaches to handle OPTIONS requests:
1. Using mod_rewrite with Header Preservation
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L,E=API_OPTIONS:1]
Header always set Access-Control-Allow-Origin "*" env=API_OPTIONS
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS" env=API_OPTIONS
2. Using LocationMatch Directives
<LocationMatch "^/api/">
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* - [E=HTTP_ORIGIN:%{HTTP:ORIGIN},L]
Header always set Access-Control-Allow-Origin "%{HTTP_ORIGIN}e" env=HTTP_ORIGIN
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS" env=HTTP_ORIGIN
Header always set Access-Control-Allow-Headers "Content-Type" env=HTTP_ORIGIN
</LocationMatch>
3. The Most Reliable Apache 2.4+ Solution
<IfModule mod_headers.c>
<If "%{REQUEST_METHOD} == 'OPTIONS'">
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header always set Access-Control-Allow-Headers "Content-Type"
Header always set Content-Length 0
Header always set Content-Type "text/plain; charset=utf-8"
Return 200
</If>
</IfModule>
Verify with curl:
curl -X OPTIONS http://yourdomain.com/api/endpoint -i
Should return:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Content-Length: 0
Content-Type: text/plain; charset=utf-8
For high-traffic sites, consider:
- Moving CORS logic to CDN configuration
- Using nginx for OPTIONS handling if fronting Apache
- Implementing caching for OPTIONS responses
When implementing Cross-Origin Resource Sharing (CORS), browsers automatically send HTTP OPTIONS requests as preflight checks before making actual cross-domain requests. While we've correctly set the CORS headers in Apache:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
The challenge arises when we need Apache to immediately respond with 200 OK to OPTIONS requests without executing backend scripts.
Using mod_rewrite might seem logical but causes header loss:
# This approach doesn't preserve headers
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
The proper way is using Apache's LimitExcept
or RewriteRule
with special flags:
<Location "/">
# First set the CORS headers
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header always set Access-Control-Allow-Headers "Content-Type"
# Then handle OPTIONS requests
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L,E=HTTP_OPTIONS:%{REQUEST_METHOD}]
</Location>
For simpler cases, this works effectively:
<LimitExcept GET POST>
Require all granted
# Return 200 for OPTIONS without processing
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* - [L]
</LimitExcept>
Verify with curl:
curl -X OPTIONS -I http://yourserver.com/api
Should return:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS