Requex.me LogoRequex.me

Documentation

Browse by section

Keep all guides, tool docs, automation recipes, and comparison pages in one navigable place.

Docs Home
Docs

Foundation docs for getting started fast, understanding key terms, and tracking what has changed.

Guides

Start with fundamentals, then move into provider-specific webhook testing and production hardening.

Tool Docs

These pages explain what each tool does, when to use it, and how it fits into a webhook debugging workflow.

Automation Docs

Use these setup guides when you want forwarding rules, custom responses, security checks, or multi-destination fanout.

Compare

Use these pages to compare developer workflows, pricing tradeoffs, and feature differences between webhook tools.

Webhook Testing in PHP

Receive webhook POST requests in PHP, verify HMAC signatures, and respond quickly — from basic handler to production-ready patterns.

Editorially reviewed by the Requex team10 min readAbout the product

TL;DR: In PHP, read the raw request body with file_get_contents('php://input') before any JSON decoding — this is required for HMAC signature verification. Return a 200 response immediately and process asynchronously if needed.

Basic PHP Webhook Handler

PHP webhook handlers are just plain PHP scripts. The key rule: call file_get_contents('php://input') as the very first operation — before json_decode() — to preserve the raw bytes for signature verification. Calling $_POST or json_decode() first does not destroy the stream in PHP, but consistently reading raw first is the safest habit.

<?php
// Read raw body before json_decode (required for HMAC)
$rawBody = file_get_contents('php://input');
$payload = json_decode($rawBody, true);

$eventType = $_SERVER['HTTP_X_EVENT_TYPE'] ?? '';

// Respond quickly
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['received' => true]);

HTTP headers arrive in $_SERVER with the prefix HTTP_, uppercased, and with hyphens replaced by underscores. So the header X-Event-Type becomes $_SERVER['HTTP_X_EVENT_TYPE'].

HMAC Signature Verification in PHP

PHP's built-in hash_hmac() and hash_equals() functions are everything you need. Use hash_equals() rather than === to prevent timing attacks:

<?php
function verifyWebhookSignature(string $rawBody, string $signature, string $secret): bool {
    $expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $signature);
}

$rawBody  = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE_256'] ?? '';
$secret   = getenv('WEBHOOK_SECRET');

if (!verifyWebhookSignature($rawBody, $signature, $secret)) {
    http_response_code(401);
    exit('Invalid signature');
}

$payload = json_decode($rawBody, true);
// Process $payload...
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['received' => true]);

Async Processing in PHP

PHP is synchronous by default, but you can flush the response to the client while continuing to execute code. The output-buffering trick below sends the 200 and closes the connection before running your slow processing logic:

<?php
$rawBody = file_get_contents('php://input');
$payload = json_decode($rawBody, true);

// Send the response immediately
http_response_code(200);
header('Content-Type: application/json');
header('Connection: close');
$body = json_encode(['received' => true]);
header('Content-Length: ' . strlen($body));
echo $body;

// Flush and close the connection
if (ob_get_level()) ob_end_flush();
flush();

// Now do slow work — the client has already received 200
ignore_user_abort(true);
processWebhookEvent($payload);

For production workloads, prefer pushing to a proper queue (Redis + a worker, SQS, or a job table in your database) rather than relying on the flush trick — it's fragile and depends on the web server respecting the flush. The output-buffer approach works well for low-traffic hooks or simple scripts.

Using Requex During Development

PHP development often runs on a local server (XAMPP, Laravel Valet, DDEV) that isn't publicly reachable. Instead of setting up a tunnel, point the webhook provider at a Requex.me endpoint first.

Requex captures the exact payload the provider sends — including all headers and the raw body. You can then replay that exact request against your local PHP script with curl:

curl -X POST http://localhost/webhook.php \
  -H "Content-Type: application/json" \
  -H "X-Signature-256: sha256=abc123..." \
  -d '{"id":"evt_123","type":"payment.succeeded"}'

This means you can iterate on your handler without triggering a real transaction in the provider each time.

Inspect the Exact Payload Before You Code

Capture what the provider actually sends — headers, raw body, signature format — before writing a single line of PHP.

Open Requex →

Related guides

Start Testing Webhooks Now

Generate your unique URL and test webhooks instantly. Free, no signup.

Open Webhook Tester →