405 Error on PUT Request to PHP Script with mod-rewrite - php

I built an API using PHP running on Apache on a CentOS box. I am trying to make a PUT request to v1/object/{objectID}/subobject/{subobjectID} but I am getting a 405 error. When I make a GET request to the same endpoint it works. When I make a PUT request to v1/object/{objectID} it works. To simplify things I replaced all of the code in api.php with a simple echo statement.
Contents of api.php:
<?php
echo "got here";
?>
Contents of .htaccess:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule v1/(.*)$ v1/api.php?request=$1 [QSA,NC,L]
</IfModule>
Below is the PUT request I am making with curl:
curl -i -X PUT -d '{"var1":"val1","var2":"val2"}' "http://x.x.x.x/api/v1/object/1/subobject/1?apiKey=somekey&secretToken=secret"
The results are as follows:
HTTP/1.1 405 Method Not Allowed
Date: Fri, 15 Dec 2017 03:31:21 GMT
Server: Apache/2.2.15 (CentOS)
Allow: GET,HEAD,POST,OPTIONS,TRACE
Content-Length: 359
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>405 Method Not Allowed</title>
</head><body>
<h1>Method Not Allowed</h1>
<p>The requested method PUT is not allowed for the URL /api/v1/object/1/subobject/1.</p>
<hr>
<address>Apache/2.2.15 (CentOS) Server at x.x.x.x Port 80</address>
</body></html>

Try the following changes on your server:
Open file "/etc/httpd/conf/httpd.conf" And look for the following:
<Limit GET POST OPTIONS>
Order allow,deny
Allow from all
</Limit>
If it is commented out, remove the # and add the PUT option
<Limit GET POST OPTIONS PUT>
Order allow,deny
Allow from all
</Limit>
Then just save, restart the server and test.

It turns out that I was making a call where the API used to be stored on the server - not to where it currently exists. Too much copy and paste, not enough attention to detail. After discovering this extremely embarrassing mistake I realized one reason I didn't think to re-check my URL was that I was getting a 405 Method Not Allowed error code, not a 404 Not Found error code..
In the interest of learning something at the expense of my confidence and my sanity I have opened a new question to find the answer to this here on Server Fault: https://serverfault.com/questions/888400/why-does-apache-return-a-405-error-code-on-a-put-request-to-a-file-or-directory

Related

Is there a way to allow dots in request urls in php?

when a request url has a dot in it I am getting a 404 error Not found page that says "The requested resource /error.example was not found on this server." What causes this error and is there a way of fixing it? I am using the Slim framework and run my code with the php built-in server. I will put bellow a sample code that recreates the above error.
index.php:
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/{name}/oth', function (Request $request, Response $response, array $args) {
$name = $args['name'];
$response->getBody()->write($name);
return $response;
});
$app->run();
.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
Both index.php and .htaccess are located inside the public folder, and i run my code with php -S localhost:8080 -t public/
When I go to my browser and go type the address http://localhost:8080/name/oth it shows name correctly but when i type http://localhost:8080/name.ex/oth it shows the error i mentioned
Not Found
The requested resource /name.ex/oth was not found on this server.
Edit:
Adding the requested information:
-Log for php -S localhost:8080 -t public/:
PHP 8.1.2 Development Server (http://localhost:8080) started
-Log for the request url http://localhost:8080/name/oth:
[::1]:63906 [200]: GET /name/oth
-Log for the request url http://localhost:8080/name.ex/oth:
[::1]:63917 [404]: GET /name.ex/oth - No such file or directory
If i replace index.php with echo 'here'; die; it displays "here" correctly in the browser.
PHP is responsible there, not slim
[::1]:63917 [404]: GET /name.ex/oth - No such file or directory
This log message originates from the php built-in web server, not slim - it's implicit from "No such file or directory" that the request has been considered a file path.
If the response looks like this (php purple :)), i.e. different from anything your own script might generate, that's also strong evidence it's coming from the built-in web server.
This is very likely related to the known behavior of the php dev server considering any url with a . in it as a filepath and not forwarding the request to any php script - see for example this old issue.
Specify a router script
The fix here is simple, specify a "router script" when starting the php development server:
If a PHP file is given on the command line when the web server is started it is treated as a "router" script.
This is also how slim's own documentation say to use the webserver, adapted to the question use:
$ php -S localhost:8080 -t public/ public/index.php
With the server running with an explicit router script, that should resolve the immediate problem of requests not reaching index.php:
$ curl -i http://localhost:8080/blah.ex/blah
HTTP/1.1 200 OK
Host: localhost:8080
Date: Sun, 20 Mar 2022 20:33:58 GMT
Connection: close
X-Powered-By: PHP/8.0.11
Content-type: text/html; charset=UTF-8
...whatever public/index.php returns...

404 error for 1 Slim route on GoDaddy while it works perfectly on localhost

I have a multiple GET, POST and PUT routes declared in my Slim v2 App and the API had been working perfectly until today. Out of 2 POST routes, the first one declared is returning a 404 Not Found error. This only happens when I call the API on my GoDaddy hosting; when I test it on my localhost it works fine. This is so weird I have ran out of ideas on how to debug this problem. I am using Postman to execute and test the calls to both of servers, local and GoDaddy. Also, the calls to the GoDaddy hosted API are done via HTTPS. My .htaccess file on both servers looks like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L,QSA]
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Pastebin to curl calls
Screenshot of Postman call
Pastebin to code fragment
When doing a POST request to /apifolder/v1/addcheckin server responds with 301 redirect to which causes the client to make a GET request to /apifolder/v1/addcheckin. Since there is no such GET route defined server responds with 404 error.
$ curl -i -X POST https://myurl.com/apifolder/v1/addcheckin
HTTP/1.1 301 Moved Permanently
Date: Sun, 26 Feb 2017 02:26:34 GMT
Location: https://myurl.com/apifolder/v1/addcheckin
Check the middleware and the rest of the code to see what causes the 301 redirect.

How to pass post data after url rewrite in .htaccess

I use the following code in htaccess file located in /projects/testsite
AddType x-mapp-php5 .php
RewriteEngine On
RewriteBase /
Options -MultiViews
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . projects/testsite/index.php [L,QSA]
#ErrorDocument 404 /404.php
when i am in http://www.mydomain.com/projects/testsite/admin/articles/1/edit
and i press Save which redirects the request to http://www.mydomain.com/projects/testsite/admin/articles/1/save
all post data are lost.
What i get if i try to debug is
POST: Array print_r: Array ( )
GET: Array print_r: Array ( )
What should i do to my .htaccess file to keep redirecting all requests to index.php but preserving all post data?
Thank you in advance!
P.S. my site works normally if i set it in a Windows Server with web.config rewrite rules.
UPDATE #1
From Firefox Live HTTP headers i see a significant issue: a 301 Moved Permanently header is captured only in Apache (this is not happening in IIS)
HTTP/1.1 301 Moved Permanently
Date: Sun, 06 Apr 2014 13:48:06 GMT
Server: Apache
Location: http://mydomain.gr/projects/testsite/admin/articles/6/save
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 270
Keep-Alive: timeout=3, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
UPDATE #2
It seems that there is some relation with this issue:
How to preserve POST data via ajax request after a .htaccess redirect?
where the asker finds out that someone was forcing a 301 redirect rule for every request on top of the .htaccess file.
Is it the hosting provider to blame?
This was quite baffling, hope i saved someone from the same headache:
The problem was located in Parallels Panel>Websites & Domains>mydomain.gr> Hosting Settings
Locate the select field:
Preferred domain, it was set to domain.tld but all my requests where to www.domain.tld so a 301 redirection (you lose all post data) was forced by parallels and not by my applications files.
Note that the label in Parallels warns that:
Regardless of the domain's URL that visitors specify in a browser (with the www prefix or without it), a page with the preferred domain's URL opens. The HTTP 301 code is used for such a redirection. The 'None' value means that no redirection is performed.
So my solution was to change Preferred domain to None.

PUT request returns 406 (Not acceptable) coming from Backbone.js to my REST-ful PHP page

I wrote a REST-ful API using Fat Free Framework in PHP, and am making a call using backbone.js. When I try and save a new Orders model my app makes a PUT request, and the server spits back a 406 error.
Request Method:PUT
Status Code:406 Not Acceptable
Request Headers
Accept:application/json, text/javascript, */*; q=0.01
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:174
Content-Type:application/json
Cookie:__utma=239804689.76636928.1286699220.1305666110.1325104376.94; __utmz=239804689.1325104376.94.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); PHPSESSID=935d2632fd0d12a1a0df4cb0f392eb5e
X-Requested-With:XMLHttpRequest
Request Payload
{"id":0,"customerId":0,"lastPage":"items","priceConfig":null,"items":null,"saveStatus":0,"savedAt":1326588395899,"name":null}
Response Headers
Connection:Keep-Alive
Content-Length:460
Content-Type:text/html; charset=iso-8859-1
Date:Sun, 15 Jan 2012 00:46:37 GMT
Keep-Alive:timeout=5, max=98
Server:Apache
My .htaccess file looks like this:
# Enable rewrite engine and route requests to framework
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L,QSA]
# Disable ETags
<IfModule mod_headers.c>
Header Unset ETag
FileETag none
</IfModule>
# Default expires header if none specified (stay in browser cache for 7 days)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault A604800
</IfModule>
<IfModule mod_security.c>
SecFilterEngine Off
SecFilterScanPOST Off
</IfModule>
My website application works fine on my local server and only does this on my web server. Any ideas what is going wrong?
I came up with a workaround.
My believe my server is using mod_security2 to block PUT and DELETE requests. I am waiting to hear back from them, and mod_security2 can't be disabled in .htaccess files so there's nothing I can do.
Using "Script PUT /filename" in the .htaccess file was causing a 500 error: "Script not allowed here", I am not sure why, but I decided not to deal with having my web host reconfigured to handle PUT and DELETE.
To keep my API REST-ful, I left in the normal handling of PUT and DELETE, and added this to the POST handling:
function post() {
//if Backbone.emulateHTTP is true, emulate PUT
$data = json_decode(F3::get('REQBODY'), true);
$type = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; //PUT, DELETE, POST
if ($type == 'PUT') {
$this->put();
return;
}
if ($type == 'DELETE') {
$this->delete();
return;
}
//handle normal POST here
}
If you set Backbone.emulateHTTP = true; it keeps the request method as POST and sends the X-HTTP-Method-Override as PUT or DELETE.
I like this because I can keep my REST-ful implementation intact, and just comment out the emulateHTTP code for when I publish to my webserver.

Apache and backslashes in mod_rewrite

I want to process all incoming requests through a single script (index.php in web-root).
So, the following is what currently happens: http://localhost/foo/bar/baz
Is routed by Apache (through .htaccess) to: http://localhost/index.php?url=foo/bar/baz
This works well, however, in Firefox I am able to do this: http://localhost/foo\ -> notice the backslash.
And Apache, instead of doing: /index.php?url=foo\
Emits a generic error page saying:
Object not found! The requested URL
was not found on this server. If you
entered the URL manually please check
your spelling and try again.
If you think this is a server error,
please contact the webmaster.
Error 404 localhost Apache/2.2.14
(Win32) DAV/2 mod_ssl/2.2.14
OpenSSL/0.9.8l mod_autoindex_color
PHP/5.3.1 mod_apreq2-20090110/2.7.1
mod_perl/2.0.4 Perl/v5.10.1
Directly going to: http://localhost/index.php?url=foo\ works without issues, however.
All the sites that I've seen on the internet seem to be able to handle backslashes gracefully (e.g., http://stackoverflow.com/tags/php\\\\\).
I consider this behavior a bug and I want to force Apache to forward backslashes correctly.
Here's my .htaccess file in its entirety:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [L]
How can I make this work properly?
Edit: I just tried this on my webhost and this is handled properly by them. Makes me think it's some setting in httpd.conf.
Probably need AcceptPathInfo off to not get those tolerated as forward slashes.
I was just having this issue on WampServer on Windows. This page had the solution - the AllowEncodedSlashes option that RobertPitt mentions above is the correct one, but it must go in the http.conf file, inside a VirtualHost block, eg:
<VirtualHost *:80>
AllowEncodedSlashes On
</VirtualHost>
It doesn't work in a .htaccess file.

Categories