Dreaded CORS issue on netlify function

I have several netlify functions configured. One of them, just one of them, is giving me the dreaded HTTP 302 response CORS error when consumed from a browser on another domain.

All my netlify function endpoints are configured in the netlify.toml file and all follow the following format:

[[redirects]]
  from = "/api/gift/:txHash"
  to = "/api/_gift?txHash=:txHash"
  status = 301

[[redirects]]
    from = "/api/_gift"
    to = "/.netlify/functions/gift"
    force = true
    status = 200

All of them work correctly when consumed from a browser on a different domain except for one.

The one that does not work can be fetched from Postman just fine (HTTP 200 response). When I do so I can clearly see the response headers correctly being sent.

const headers = { 
  'Content-Type': 'application/json',
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Content-Type',
  'Access-Control-Allow-Methods': 'GET, POST, OPTION',
};

exactly as coded.

And yet one of them, just one, returns the HTTP 302 CORS error when invoked from a browser from a different domain. Why? How can this be resolved?

The issue can be seen clearly here. Invoking https://cryptomint.one/api/gift/some-test-tx-hash from Postman returns a valid JSON HTTP 200 response and valid access-control headers. Invoked from a browser (in this case from localhost) and this is observed.

UPDATE

If I hit https://cryptomint.one/.netlify/functions/gift?txHash=some-test-tx-hash directly from the browser it works with an HTTP 200 response. So something to do here with the redirects configuration not playing nicely for some reason. Note that hitting netlify/functions directly is not a solution.

Could you share the link of the page calling the API endpoint?

I’m calling it from localhost

if you are calling it from localhost, it makes sense that you would be receiving a CORS issue. if you deploy, are you able to call the function?

Not so. The Access-Control-Allow-Origin = "*" response header is there to allow access from anywhere.

So my issue is this. /api/gift/:txHash fails with a CORS error HOWEVER if I hit /.netlify/functions/gift?txHash=:txHash directly it works perfectly.

This fact means there MUST then be something wrong with my redirect configuration in the toml file (see above) OR alternatively some other issue going on.

That’s what I want to resolve but I have been unable to despite many many hours of effort.

This may have absolutely nothing to do with it, but have you tried testing it without the 301 redirect?

Along the lines of:

[[redirects]]
    from = "/api/gift/:txHash"
    to = "/.netlify/functions/gift?txHash=:txHash"
    status = 200

If I make this change then the endpoint https://cryptomint.one/api/gift/some-test-tx-hash returns a 404. Which is not at all what I would have hoped for.

Hitting https://cryptomint.one/.netlify/functions/gift?txHash=some-test-tx-hash works as expected

That seems like it should work to me, but looking at this thread there may be some limitations with rewrites to functions I wasn’t aware of:

Also, I can see a weird configuration.

You’ve this:

[[redirects]]
  from = "/api/gift/:txHash"
  to = "/api/_gift?txHash=:txHash"
  status = 200

[[redirects]]
  from = "/api/_gift"
  to = "/.netlify/functions/gift"
  force = true
  status = 200

You’re using double proxy, that is /api/gift/ to /api/_gift and then /api/_gift to /.netlify/functions/gift. This won’t work. We only allow a single proxy on a request. Otherwise it results in a 404.

Also, you’ve this:

[[headers]]
  for = "/.netlify/functions/*"
  [headers.values]
    Access-Control-Allow-Origin = "*"
    Access-Control-Allow-Headers = "Content-Type"
    Access-Control-Allow-Methods = "GET, HEAD, PUT, POST, OPTION"

This has no effect and is redundant as Functions are unaffected by these headers.

I think you can simplify the rewrite by:

[[redirects]]
  from = "/api/gift"
  to = "/.netlify/functions/gift"
  force = true
  status = 200

200 rewrites preserve query string parameters so you could make a request like: /api/gift?txHash=some-test-tx-hash and it would redirect fine.

@hrishikesh Can you confirm why the following doesn’t work? (For my own interest)

[[redirects]]
    from = "/api/gift/:txHash"
    to = "/.netlify/functions/gift?txHash=:txHash"
    status = 200

Does the rewrites engine not support placeholder values being passed into named query string parameters?

I’m also not sure why force would be necessary, (unless there is already a file at the specified from path).

From my testing it does not @nathanmartin.

Agree with @hrishikesh’s final redirect and usage example @eddyoc which is the only way I have found to make this work.

This is precisely how I originally configured my endpoints. Alas it did not work. Hence the move to more convoluted approaches. After a bit of testing I can see

https://cryptomint.one/api/gift/some-test-tx-hash

does not work however

https://cryptomint.one/api/gift/some-test-tx-hash?txHash=some-test-tx-hash

does. Which is far from ideal as we want to take path params and transform into querystring params.

This appears to be a standard pattern on netlify functions. However I have not found configuration that consistently works.

It works that way with 301/302, not with 200. As to why, I don’t know precisely, but that’s a great question for the devs. I’d ask them and revert.

Yeah, it’s not necessary and can be ignored.

You don’t need to make it double, it can be simply like:

https://cryptomint.one/api/gift/?txHash=some-test-tx-hash

Query String Parameters that already exist in a URL will be passed on as is. Even if you need to access the path inside your functions, you can make it like event.path + '/' + event.queryStringParameters.txHash.

Thanks for clarifying @coelmay & @hrishikesh.

I agree with @eddyoc that the current inability to alias placeholder values in as query string parameters isn’t ideal, especially if it behaves in an undocumented inconsistent fashion by working with 302/301 but not 200.

I was going to lodge an issue against this repository:

But a quick check makes me believe the issue isn’t the parsing, since this:

/blog/:post_id /blog?post=:post_id  200

Becomes this:

{
  from: '/blog/:post_id',
  query: {},
  to: '/blog?post=:post_id',
  status: 200,
  force: false,
  conditions: {},
  headers: {},
  path: '/blog/:post_id',
  proxy: false
}

Which looks correct to me, at least compared to the results of the other unit tests.
In particular it isn’t inadvertently stripping the query string parameters which I’d though may have been the case.

So the limitation/issue could be occuring after the parsing at the implementation level.
Is there a better spot that I can lodge a ticket so it doesn’t get forgotten?

Possible file an issue on the parser you have linked to.

So in the end I simply ran with the simple configuration

[[redirects]]
    from = "/api/gift/:txHash"
    to = "/.netlify/functions/gift/:txHash"
    status = 200

The parameters are not exposed from event.pathParameters in the handler unfortunately but wrote my own url parser to extract them from the rawUrl. Suboptimal but unblocks us until a more robust solution comes along.

Yeah, that’s what the team said to the question I asked them before my weekend. Functions will only see the original URL where the request was made, and not see the resolved URL, thus the problem.

@nathanmartin have you filed the issue somewhere? If not, I’ll be filing one for the team to work on and would like to avoid duplicating it.

@hrishikesh Thanks for the follow up.

I haven’t had the chance to lodge an issue anywhere as I’ve been handling unrelated client work, it’d be great it you could file it in the appropriate spot for the team to look at.

I’d completely understand if it can’t be solved easily too, in which case it might just be worth mentioning the limitation in the docs.

Thanks for the feedback. I’ve added this to an internal issue and we would follow up if something changes.

I’m also passing your feedback on to the docs team.