Search code examples
phphttp-headerscorssilex

CORS preflight request returning HTTP 405


I am trying to create a RESTful web service and have gotten stuck on implementing PUT requests. I have tried and failed to follow other answers on this site and various articles from Mozilla.

The request is generated from the domain wwwtest.dev-box and it's going to test.dev-box (basically a front-end app calling the back-end app). Here are the headers I have captured from Live HTTP headers:

http://test.dev-box/resource/v1/data/user/1

OPTIONS /resource/v1/data/user/1 HTTP/1.1
Host: test.dev-box
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://wwwtest.dev-box
Access-Control-Request-Method: PUT
Connection: keep-alive

HTTP/1.1 405 Method Not Allowed
Date: Wed, 16 Oct 2013 16:15:58 GMT
Server: Apache/2.2.15 (Red Hat)
x-powered-by: PHP/5.3.27
Access-Control-Allow-Origin: http://wwwtest.dev-box
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 1728000
Content-Length: 0
Allow: PUT
Cache-Control: no-cache
Connection: close
Content-Type: text/html; charset=UTF-8

I'm using a framework so everything is routed to web.php, which contains the following code at the top of the page (taken from this MDN article):

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    header('Access-Control-Allow-Origin: http://wwwtest.dev-box');
    header('Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS');
    header('Access-Control-Max-Age: 1728000');
    header("Content-Length: 0");
    header("Content-Type: text/plain");
} else {
    header("HTTP/1.1 403 Access Forbidden");
    header("Content-Type: text/plain");
}

Before my code makes the PUT request it sends the CORS preflight OPTIONS request first (as you can see from the capture above). The necessary headers should be attached to the response and tell the requester that PUT is allowed. Unfortunately as you can see from the response headers above it is still returning 405 Method Not Allowed even though PUT is in the response access-control-allow-methods.

This is the .htaccess file for the framework I'm using (Silex):

<IfModule mod_rewrite.c>
    Options -MultiViews

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ web.php [QSA,L]
</IfModule>

Solution

  • I've found the answer to be a cross between Apache and framework configuration.

    1. For the Apache config you can either put the following into your VirtualHost directive, or in the .htaccess file of the requested domain (if it's in the .htaccess remember to encapsulate with IfModule mod_headers.c tags). Setting the headers on the page that mod_rewrite redirects to does not work:
        Header set Access-Control-Allow-Origin "http://wwwtest.dev-box"
        Header set Access-Control-Allow-Methods "GET,POST,HEAD,DELETE,PUT,OPTIONS"
    
    1. For the Silex config, place the following into your application routes. It basically sends an HTTP 200 OK for any OPTIONS request it receives. Code found on the Silex Google Group:
        $app->match("{url}", function($url) use ($app) {
            return "OK";
        })->assert('url', '.*')->method("OPTIONS");
    

    Both steps need to be completed for the RESTful application to function properly.