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

geertjanknapen1's avatar

Shopify webhooks slow.

I'm implementing Shopify webhooks, my current setup works, but it is too slow. Resulting in the deletion of the webhook on Shopify's end. Shopify states

Shopify waits five seconds for a response to each request to a webhook. If there's no response, or an error is returned, then Shopify retries the connection 19 times over the next 48 hours. If there are 19 consecutive failures, then the webhook subscription is automatically deleted. A warning that the subscription will be deleted is sent to the app's emergency developer email address.

My controller takes roughly 30 seconds to respond to the webhook which causes Shopify to delete the webhook after 48 hours since it takes us longer than 5 seconds to respond, I'm looking for a way to speed this up but I am unable to come up with a solution, because the controller does not do anything resource intensive.

My controller looks as follows:

public function handleWebhook($shopname, $hash)
{
   		$shopDomainHeader = $_SERVER['HTTP_X_SHOPIFY_SHOP_DOMAIN'];
        $data = file_get_contents('php://input');
        $topic = WebhookTopic::from($_SERVER['HTTP_X_SHOPIFY_TOPIC']);
        $verified = $this->verifyWebhook($shopDomainHeader, $hash);

        if (!$verified) {
            return Response::HTTP_UNAUTHORIZED;
        }

        $decodedData = json_decode($data, true);

        HandleWebhookJob::dispatch($decodedData, $shopName, $topic)->onQueue('webhook');

        return Response::HTTP_OK;
}

I know that $verified is true and the job is dispatched, but I feel that above code should not take 30 seconds. How could I speed this up?

Additional information;

  • Laravel version: 10.12.0
  • My webhook route implements the API middleware.
0 likes
6 replies
Snapey's avatar

the code you show should take milliseconds, but it depends if your site has queue workers to run the jobs or if they are running sync.

Even then, what on earth are you doing in HandleWebhookJob that could take 30 seconds?

1 like
geertjanknapen1's avatar

@Snapey Hey Snapey thanks for the answer.

That's what I thought too. Jobs are processed using redis.

But then again, if I am not mistaken, the dispatch should be asynchronous right? So the Response::HTTP_OK should not wait until the job is dispatched? Or am I wrong in this regard?

HandleWebhookJob runs a switch over the topic and based on that it runs an artisan command (in the case of product create / product update) or it deletes a directory. Then it calls another route and returns a boolean.

geertjanknapen1's avatar

Also, the funny thing is, when testing with Postman, it takes roughly 180ms to process the webhook. It goes through the same flow, only thing it does not do is properly verify the webhook.

Snapey's avatar

@geertjanknapen1 so what makes you think it takes 30 seconds?

A couple of simple log statements will tell you more.

public function handleWebhook($shopname, $hash)
{
		Log::info('webhook called');

   		$shopDomainHeader = $_SERVER['HTTP_X_SHOPIFY_SHOP_DOMAIN'];

//
		Log::info('webhook done');

        return Response::HTTP_OK;
}

geertjanknapen1's avatar

@Snapey Yes I did this, I logged the time right before getting the shopDomainHeader and right before the Response::HTTP_OK return.

Logs show;

Webhook received {"time": "12:02:36pm"}

Response sent {"time": "12:03:07pm"}

Webhook received {"time": "12:04:43pm"}

Response sent {"time": "12:05:14pm"}

So roughly 30 seconds apart.

geertjanknapen1's avatar

@Snapey Currently the HandleWebhookJob handles all the webhooks (create, update, delete). Do you think it would help performance if we had an individual job for each of the topics? That way we'd have to move the logic to decide what type of job should be created to the controller, but I guess that would not have as much of an impact on the job dispatching than it currently has?

Please or to participate in this conversation.