Handling preflight requests

Hello,

I’m developing a is a simple cross-site API worker and the front-end is using Axios to handle the requests.

Axios, by default, sends a preflight OPTIONS request to check the CORS headers. Currently, I have disabled the default behavior. However, I would like to have it handle the request correctly.

Is there talk of supporting the OPTIONS request? And POST bodies of application/json?

Thanks!

Are you experiencing issues with Axios or any of the mentioned HTTP request schemes? All of these should be supported with Cloudflare.

Axios wouldn’t make the POST request, because the OPTIONS request was failing. (Not 100% sure my code wasn’t working, mainly because the testing tab doesn’t have OPTIONS as an option.)

I’ve switched Axios to not do application/json POST requests, so it is hitting the worker. In testing tab it works 100% as expected. In the real word, the request hangs, then times out.

Hi @webchad,

We’ll add the OPTIONS method to the testing tab as soon as possible. The Workers runtime environment itself supports the OPTIONS method, so you can implement the CORS protocol in your worker, but you’d currently need to test its functionality while it’s deployed in production.

Here’s a boilerplate worker that implements a minimal amount of the CORS protocol to get you started:

// An example worker which supports CORS. It passes GET and HEAD
// requests through to the origin, but answers OPTIONS and POST
// requests directly. POST requests must contain a JSON payload,
// which is simply echoed back.

addEventListener('fetch', event => {
  event.respondWith(handle(event.request)
      // For ease of debugging, we return exception stack
      // traces in response bodies. You are advised to
      // remove this .catch() in production.
      .catch(e => new Response(e.stack, {
        status: 500,
        statusText: "Internal Server Error"
      }))
  )
})

async function handle(request) {
  if (request.method === "OPTIONS") {
    return handleOptions(request)
  } else if (request.method === "POST") {
    return handlePost(request)
  } else if (request.method === "GET" || request.method == "HEAD") {
    // Pass-through to origin.
    return fetch(request)
  } else {
    return new Response(null, {
      status: 405,
      statusText: "Method Not Allowed",
    })
  }
}

// We support the GET, POST, HEAD, and OPTIONS methods from any origin,
// and accept the Content-Type header on requests. These headers must be
// present on all responses to all CORS requests. In practice, this means
// all responses to OPTIONS or POST requests.
const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type",
}

function handleOptions(request) {
  if (request.headers.get("Origin") !== null &&
      request.headers.get("Access-Control-Request-Method") !== null &&
      request.headers.get("Access-Control-Request-Headers") !== null) {
    // Handle CORS pre-flight request.
    return new Response(null, {
      headers: corsHeaders
    })
  } else {
    // Handle standard OPTIONS request.
    return new Response(null, {
      headers: {
        "Allow": "GET, HEAD, POST, OPTIONS",
      }
    })
  }
}

async function handlePost(request) {
  if (request.headers.get("Content-Type") !== "application/json") {
    return new Response(null, {
      status: 415,
      statusText: "Unsupported Media Type",
      headers: corsHeaders,
    })
  }

  // Detect parse failures by setting `json` to null.
  let json = await request.json().catch(e => null)
  if (json === null) {
    return new Response("JSON parse failure", {
      status: 400,
      statusText: "Bad Request",
      headers: corsHeaders,
    })
  }

  return new Response(JSON.stringify(json), {
    headers: {
      "Content-Type": "application/json",
      ...corsHeaders,
    }
  })
}

Harris

5 Likes

Thanks @harris

I’ll give this a go now!

@harris any word on when this will be on the testing tab?

It is 100% working in my actual worker.

Hi @webchad!

I had expected it would be deployed by now. I have pinged the appropriate people internally – I’ll know more tomorrow and will update you.

Harris

1 Like

Looks like you got this resolved but I want to leave this here for anyone else and for future reference: