How to Configure Apache to Return 200 OK for HTTP OPTIONS Requests (CORS Preflight Handling)


3 views

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