Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mark.p.ramos's avatar

POST request arrives as GET, returns 405: Method Not Allowed

I've defined an endpoint to receive POST requests from SendGrid's Parse API. My issue is that many of these requests return "405: Method Not Allowed" to SendGrid.

If I open the endpoint to all request methods, it never generates 405 errors. However by the time execution reaches my controller method $request->method() often returns 'GET' and all of the expected POST parameters are missing.

  1. I have confirmed with SendGrid they ONLY send POST requests
  2. I have setup a RunScope endpoint to capture all their requests and independently confirmed they ONLY send POST requests
  3. Below is a recent stretch from my access log
167.89.125.253 - - [14/Aug/2016:14:57:34 -0400] "POST /endpoints/sendgrid/mail/email HTTP/1.1" 405 4485 "-" "SendGrid 1.0"
167.89.125.221 - - [14/Aug/2016:14:58:01 -0400] "POST /endpoints/sendgrid/mail/email HTTP/1.1" 405 4485 "-" "SendGrid 1.0"
167.89.125.235 - - [14/Aug/2016:15:06:04 -0400] "POST /endpoints/sendgrid/mail/email HTTP/1.1" 405 4485 "-" "SendGrid 1.0"
167.89.125.254 - - [14/Aug/2016:15:14:21 -0400] "POST /endpoints/sendgrid/mail/email HTTP/1.1" 204 - "-" "SendGrid 1.0"
167.89.125.225 - - [14/Aug/2016:15:21:14 -0400] "POST /endpoints/sendgrid/mail/email HTTP/1.1" 405 4485 "-" "SendGrid 1.0"

The access log also indicates that all requests are POST. I don't see any redirects happening. And while sometimes the requests work as expected, most of the time they result in 405.

I am losing my mind over this one. Please help!

My route definition:

Route::group(['prefix' => 'endpoints', 'namespace' => 'Endpoints'], function() {
    Route::group(['prefix' => 'sendgrid'], function() {
            Route::group(['prefix' => 'mail'], function() {
                Route::post('email', ['uses' => 'SendGridController@incomingEmail', 'as' => 'sendGrid.mail.email']);
         });
    });

    Route::group(['prefix' => 'leads'], function() {
            Route::post('email', ['uses' => 'EmailProspectController@incomingEmail', 'as' => 'sendGrid.leads.email']);
            Route::post('prospects', ['uses' => 'EmailProspectController@addProspect', 'as' => 'sendGrid.leads.prospects']);
        });
});

My controller:

namespace App\Http\Controllers\Endpoints;

use Exception;
use Log;
use Illuminate\Routing\Controller;
use Illuminate\Http\Request;
use App\Services\SendGrid\ProxyEmailManager;
use App\Services\SendGrid\Email;
use App\Services\SendGrid\UserNotFoundException;
use App\Services\SendGrid\ProspectNotFoundException;
use App\Utility\StringUtility;


class SendGridController extends Controller {
    
    public function incomingEmail(Request $request) {
        try {
            $email = Email::fromRequest($request);
            if (StringUtility::emptyOrWhitespace($email->body())) {
                return response('email is empty', 202);
            }

            $proxyEmailManager = new ProxyEmailManager();
            if ($proxyEmailManager->isUserProxyAddress($email->to)) {
                return $this->forwardToUser($email, $proxyEmailManager);
            }

            if ($proxyEmailManager->isProspectProxyAddress($email->to)) {
                return $this->forwardToProspect($email, $proxyEmailManager);
            }
        } catch (Exception $ex) {
            Log::error($ex->getMessage(), ['stack trace' => $ex->getTraceAsString()]);
            return response($ex->getMessage(), 202);
        }
        
        return response('unrecognized proxy address', 202);
    }
}
0 likes
8 replies
Bonsi's avatar

I'm not exactly sure a CSRF token mismatch exception would return a 405, but your general description and setup sounds like it could be the problem here. Laravel expects a CSRF token to be provided for POST requests through the VerifyCsrfToken middleware, unless explicitly disabled. Try adding the "/endpoints/sendgrid/mail/email" route to the "excepts" array in app\Http\Middleware\VerifyCsrfToken.php and see if that makes a difference? Still odd though it works sometimes...

mark.p.ramos's avatar

@Bonsi I'm not using VerifyCsrfToken at all.

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class
            //\App\Http\Middleware\VerifyCsrfToken::class
        ],

In fact I'm not using any middleware on this endpoint.

mark.p.ramos's avatar
mark.p.ramos
OP
Best Answer
Level 1

I discovered that certain requests from SendGrid were triggering some Mod Security rules which were stripping all of the information out of the request and then pumping them through as GET instead of POST. What a pain!

iheo's avatar

Hello Mark! How did you solve the issue with mod security rules? I'm experiencing the same issue now, but my Apache Server has no enabled security modules.

mark.p.ramos's avatar

@iheo if you're not using ModSec, I don't see how what I did will be relevant? Doesn't sound like the cause of your problem is the same. However I believe I simply disabled the offending rules which may not even be ideal.

tradjick's avatar

A bit late for this party, but may help someone else.

I Just has a similar occurrence and was driving me nuts. POST becoming GET with all the parameters stripped. Turned out it was due to silly old me. In postman I had http not https while I had set up http to https redirect with letsencrypt... why I did that on an API and also have port 80 open, I have no idea, but I did. This led to the described behaviour

Please or to participate in this conversation.