GitHub Webhooks - PHP

Snippet Git

PHP native implementation for GitHub webhooks with signature verification and deployment

PHP - Native Handler

PHP webhook handler using native functions.

<?php
$secret = "your_secret";

// Read payload
$payload = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_HUB_SIGNATURE_256"] ?? "";

// Verify signature
$hash = "sha256=" . hash_hmac("sha256", $payload, $secret);
if (!hash_equals($hash, $signature)) {
    http_response_code(401);
    exit("Invalid signature");
}

// Handle event
$event = $_SERVER["HTTP_X_GITHUB_EVENT"] ?? "unknown";

if ($event === "push") {
    shell_exec("sh /path/to/deploy.sh");
}

echo "OK";
?>

PHP - Verify Signature

Signature verification function.

<?php
function verifyGitHubSignature($payload, $signature, $secret) {
    if (empty($signature)) {
        return false;
    }

    $hash = "sha256=" . hash_hmac("sha256", $payload, $secret);
    return hash_equals($hash, $signature);
}

// Usage
$payload = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_HUB_SIGNATURE_256"] ?? "";
$secret = "your_webhook_secret";

if (!verifyGitHubSignature($payload, $signature, $secret)) {
    http_response_code(401);
    exit("Invalid signature");
}
?>

PHP - Parse JSON Payload

Parse and process webhook payload.

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

// Get repository information
$repo = $data['repository']['name'] ?? '';
$branch = $data['ref'] ?? '';
$pusher = $data['pusher']['name'] ?? '';

// Log webhook event
error_log("Webhook received: $repo, $branch by $pusher");

// Execute deployment
if ($branch === 'refs/heads/main') {
    shell_exec("cd /var/www/html && git pull origin main");
}
?>

PHP - Complete Example

Full working PHP webhook handler with error handling.

<?php
// Configuration
$secret = getenv('WEBHOOK_SECRET') ?: 'your_secret';
$deploy_script = '/path/to/deploy.sh';
$log_file = '/var/log/github-webhook.log';

// Read payload
$payload = file_get_contents("php://input");
$signature = $_SERVER["HTTP_X_HUB_SIGNATURE_256"] ?? "";
$event = $_SERVER["HTTP_X_GITHUB_EVENT"] ?? "unknown";

// Log request
file_put_contents($log_file, date('Y-m-d H:i:s') . " - Event: $event\n", FILE_APPEND);

// Verify signature
$hash = "sha256=" . hash_hmac("sha256", $payload, $secret);
if (!hash_equals($hash, $signature)) {
    http_response_code(401);
    file_put_contents($log_file, "Invalid signature\n", FILE_APPEND);
    exit("Invalid signature");
}

// Handle push event
if ($event === "push") {
    $data = json_decode($payload, true);
    $branch = $data['ref'] ?? '';

    file_put_contents($log_file, "Push to: $branch\n", FILE_APPEND);

    // Execute deployment script
    $output = shell_exec("sh $deploy_script 2>&1");
    file_put_contents($log_file, "Deploy output: $output\n", FILE_APPEND);
}

http_response_code(200);
echo "OK";
?>

Run - Development

Run PHP development server.

# Start built-in server
php -S 0.0.0.0:8000

# Test webhook
curl -X POST http://localhost:8000/webhook.php \
  -H "X-GitHub-Event: push" \
  -d '{"ref":"refs/heads/main"}'

Run - Apache

Apache configuration for PHP webhook.

<VirtualHost *:80>
    ServerName webhook.example.com
    DocumentRoot /var/www/webhook

    <Directory /var/www/webhook>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/webhook_error.log
    CustomLog ${APACHE_LOG_DIR}/webhook_access.log combined
</VirtualHost>

Run - Nginx + PHP-FPM

Nginx configuration with PHP-FPM.

server {
    listen 80;
    server_name webhook.example.com;
    root /var/www/webhook;

    location /webhook.php {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Environment Variables

Store secret in environment file.

Apache - Add to .htaccess:

SetEnv WEBHOOK_SECRET your_secret_here

Nginx - Add to site config:

fastcgi_param WEBHOOK_SECRET your_secret_here;

PHP-FPM - Add to pool config /etc/php/8.1/fpm/pool.d/www.conf:

env[WEBHOOK_SECRET] = your_secret_here

Docker Deployment

Containerize PHP webhook handler.

FROM php:8.1-apache
COPY webhook.php /var/www/html/
RUN chown -R www-data:www-data /var/www/html
EXPOSE 80

Build and run:

docker build -t github-webhook-php .
docker run -p 8000:80 -e WEBHOOK_SECRET=your_secret github-webhook-php

File Permissions

Set proper permissions for deployment script.

# Make deploy script executable
chmod +x /path/to/deploy.sh

# Give PHP user permission (if needed)
sudo chown www-data:www-data /path/to/deploy.sh

# Or add www-data to appropriate group
sudo usermod -aG deployers www-data

Logging

Enhanced logging for debugging.

<?php
function logWebhook($message) {
    $log_file = '/var/log/github-webhook.log';
    $timestamp = date('Y-m-d H:i:s');
    file_put_contents($log_file, "[$timestamp] $message\n", FILE_APPEND);
}

// Log incoming request
logWebhook("Webhook received: " . $_SERVER["HTTP_X_GITHUB_EVENT"]);

// Log verification result
if (!verifySignature()) {
    logWebhook("ERROR: Signature verification failed");
} else {
    logWebhook("SUCCESS: Signature verified");
}
?>

View logs:

tail -f /var/log/github-webhook.log