Cloudflare Workers#

This guidepage walks through the steps on how to integrate your HTTP-interaction (serverless) Discord application with Cloudflare Workers.

Prerequisites#

Before we proceed with the steps, there are prerequisites to prepare:

  1. A Cloudflare Worker.

  2. A Discord account, and a Discord application and its public key (can be found in the General Information tab of your application in the Developer Portal).

Setup#

Navigate to the parent directory of where your project directory will reside. In the terminal, set up your development environment:

Install the cryptography package for later:

uv add cryptography==47.0.0

Important

workers-py currently only supports version 47.0.0 of the cryptography.

Utilizing the pynacl package cannot be done as well, for the reason that libsodium internally uses eval, which is currently disabled in workers for security purposes; this cannot be resolved right now unless libsodium drops EM_ASM and replace it to something else.

Copy your Discord application’s public key and paste it in to your wrangler.jsonc file within the "vars" key.

{
  "vars": {
    "APPLICATION_PUBLIC_KEY": "paste your public key here"
  }
}

Configuring Interactions Endpoint URL#

An Interactions Endpoint URL is a public endpoint for your app where Discord can send your app HTTP-based interactions. If your app is using Gateway-based interactions, you don’t need to configure an Interactions Endpoint URL.

Before you can add your Interactions Endpoint URL to your app, your endpoint must be prepared for two things ahead of time:

  1. Validate security-related request headers (X-Signature-Ed25519 and X-Signature-Timestamp).

  2. Acknowledging PING requests from Discord.

Validating Security Request Headers#

To receive interactions via HTTP, there are some security steps you must take before your app is eligible to receive requests.

Each interaction is sent with the following headers:

  • X-Signature-Ed25519 as a signature

  • X-Signature-Timestamp as a timestamp

Using your favorite security library, you must validate the request each time you receive an interaction. If the signature fails validation, your app should respond with a 401 error code.

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from workers import Response, WorkerEntryPoint
import json


class Default(WorkerEntryPoint):
  async def fetch(self, request):
    if request.method != "POST":
      return Response("Method Not Allowed", status = 405)
    body = await request.text()
    signature = request.headers["X-Signature-Ed25519"]
    timestamp = request.headers["X-Signature-Timestamp"]
    try:
      verify_key = Ed25519PublicKey.from_public_bytes(bytes.fromhex(self.env.APPLICATION_PUBLIC_KEY))
      verify_key.verify(bytes.fromhex(signature), f"{timestamp}{body}".encode())
    except InvalidSignature:
      return Response("Invalid request signature", status = 401)

Acknowledging PING Requests#

When adding your Interactions Endpoint URL, Discord will send a POST request with a PING payload with a type: 1 to your endpoint. Your app is expected to acknowledge the request by returning a 200 response with a PONG payload (which has the same type: 1).

class Default(WorkerEntryPoint):
  async def fetch(self, request):
    # ...
    data = json.loads(body)
    if data["type"] == 1:
      return Response.json(
        { "type": 1 },
        headers = {
          "Content-Type": "application/json"
        },
        status = 200
      )

Next Steps#