GitHub Webhooks - PHP

Snippet Git

PHP native implementation for GitHub webhooks with signature verification and deployment

What is this? Official Docs

Event notification system that triggers HTTP callbacks when repository events occur on GitHub.

What is GitHub Webhooks?

GitHub Webhooks allow you to receive real-time HTTP notifications when events occur in your repositories. This guide shows PHP implementation with HMAC signature verification for secure automated deployments on Apache and Nginx servers.


🐘 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