Skip to content

LiveKit Webhook SHA256 Checksum Verification Fails on AWS ALB (Works Locally) #1145

@MINEGHOST007

Description

@MINEGHOST007

LiveKit Webhook SHA256 Checksum Verification Fails on AWS ALB (Works Locally)

Problem Description

LiveKit webhooks work perfectly when testing locally but fail with sha256 checksum of body does not match error when deployed behind AWS Application Load Balancer (ALB). The issue appears to be that the ALB is modifying/parsing the webhook request body before it reaches our Express.js service, breaking the cryptographic signature verification. ( I'm just forwarding it to my target group which should not break this flow, I just want to know if this can the issue ?? if yes what is the solution ) .

Environment

  • Local: Works perfectly ✅
  • AWS: Fails with ALB in front of ECS service ❌
  • Express.js: 4.x with TypeScript
  • LiveKit: Using livekit-server-sdk WebhookReceiver

Error Logs

Local (Working):

AWS ALB (Failing):

ARN
	
	2025-07-28T16:23:16.149Z
	�[32m[2025-07-28 16:23:16.149] INFO: N/A : REQUEST: 580214 POST /api/livekit/webhook �[39m
	Link 
	
2025-07-28T16:23:16.149Z
	LiveKit webhook received: {
	Link 
	
2025-07-28T16:23:16.149Z
	contentType: 'application/webhook+json',
	Link 
	
2025-07-28T16:23:16.149Z
	contentLength: '399',
	Link 
	
2025-07-28T16:23:16.149Z
	userAgent: 'Go-http-client/2.0',
	Link 
	
2025-07-28T16:23:16.149Z
	hasAuthHeader: true,
	Link 
	
2025-07-28T16:23:16.149Z
	authHeaderLength: 235,
	Link 
	
2025-07-28T16:23:16.149Z
	bodyType: 'object',
	Link 
	
2025-07-28T16:23:16.149Z
	bodyLength: 2,
	Link 
	
2025-07-28T16:23:16.149Z
	isBuffer: false,
	Link 
	
2025-07-28T16:23:16.149Z
	bodyPreview: {}
	Link 
	
2025-07-28T16:23:16.149Z
	}
	Link 
	
2025-07-28T16:23:16.149Z
	�[32m[2025-07-28 16:23:16.149] INFO: N/A : LiveKit webhook raw request received (15 bytes)�[39m
	Link 
	
2025-07-28T16:23:16.150Z
	�[31m[2025-07-28 16:23:16.150] ERROR: N/A : Error processing LiveKit webhook: sha256 checksum of body does not match�[39m
	Link 
	
2025-07-28T16:23:16.150Z
	�[32m[2025-07-28 16:23:16.150] INFO: N/A : RESPONSE: 580214 POST /api/livekit/webhook | Status: 400 | Time: 1.

Current Code

Express App Setup (app.ts)

const app: expressWs.Application = expressWs(express()).app;

// URL rewriting middleware FIRST
app.use((req, res, next) => {
  req.url = req.url.replace(/.*\/api/, "/api").replace(/.*\/ws/, "");
  next();
});

// Configure raw body parsing for LiveKit webhooks FIRST (before JSON parser)
app.use('/api/livekit/webhook', express.raw({ type: 'application/webhook+json' }));

// Body parser middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json({ limit: "10mb" }));

// CORS middleware
app.use(cors(options));

Webhook Handler (livekit.ts)

export const handleLivekitWebhook = async (req: Request, res: Response) => {
    try {
        console.log('LiveKit webhook received:', {
            contentType: req.get('content-type'),
            contentLength: req.get('content-length'),
            userAgent: req.get('user-agent'),
            hasAuthHeader: !!req.get('authorization'),
            bodyType: typeof req.body,
            bodyLength: req.body ? (Buffer.isBuffer(req.body) ? req.body.length : JSON.stringify(req.body).length) : 0,
            isBuffer: Buffer.isBuffer(req.body),
            bodyPreview: Buffer.isBuffer(req.body) ? req.body.toString('utf8', 0, 100) : req.body
        });

        // Convert to string for webhook verification
        const rawBodyString = Buffer.isBuffer(req.body) ? req.body.toString('utf8') : JSON.stringify(req.body);

        // This fails on AWS with "sha256 checksum of body does not match"
        const event = await livekitWebhookReceiver.receive(
            rawBodyString,
            req.get('Authorization')
        );

        // ... handle event
    } catch (error: any) {
        winstonLogger.error(`Error processing LiveKit webhook: ${error.message}`, error);
        res.status(400).send('Invalid webhook payload');
    }
};

AWS ALB Configuration

Current ALB Listener Rules:

Priority 3: Path = /participant/* → userology-dev-participant-tg

ALB Settings:

  • Protocol: HTTPS:443
  • Target Group: HTTP backend on port 8080
  • Health Check: Default settings

What We've Tried

  1. Different Express middleware orders
  2. Various express.raw() configurations
  3. Different content-type specifications
  4. Custom middleware to capture raw body

Questions

  1. Is this a known ALB behavior? Does ALB parse/transform JSON webhook payloads by default?

  2. Should we configure ALB differently? Are there specific target group attributes or listener settings that preserve raw request bodies?

  3. Alternative solutions?

Expected Behavior

The webhook handler should receive the exact raw bytes that LiveKit sent, allowing proper SHA256 signature verification to work on AWS just like it does locally.

Any insights on ALB behavior with webhook payloads or suggestions for preserving raw request bodies would be greatly appreciated!


Additional Context:

  • Other API endpoints work fine through the same ALB
  • Only webhook signature verification is affected
  • LiveKit sends Content-Type: application/webhook+json
  • The Authorization header with signature arrives intact

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions