37

I am using the imgur api to upload images via a node js app.

I am converting images to base64 strings and sending them via Postman works great.

I use node-fetch to make api calls.

const fetch = require('node-fetch')
...
async uploadImage(base64image) {
        try {
            const url = 'https://api.imgur.com/3/image'
            const res = await fetch(url,
                {
                    method: 'POST',
                    body: { image: base64image },
                    headers: {
                        'content-type': 'application/json',
                        'Authorization': 'Client-ID [my-client-id]',
                        'Access-Control-Allow-Headers': 'Content-Type, Authorization, Access-Control-Allow-Headers',
                        'Access-Control-Allow-Methods': 'POST',
                    }
                }
            )

            console.log(res)
        } catch(err) {
            console.log(err)
        }
    }

Error: Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field Access-Control-Allow-Headers is not allowed by Access-Control-Allow-Headers in preflight response.

I have tried many 'Access-Control-Allow-xxx' headers but none of them worked..

I assume it must be something simple that I am missing. I have been stuck on this for hours please help me.

Cristian G
  • 460
  • 1
  • 4
  • 22

5 Answers5

53

Browser restricts HTTP requests to be at the same domain as your web page, so you won't be able to hit imgur api directly from the browser without running into CORS issue.

I am converting images to base64 strings and sending them via Postman works great.

That's because Postman is not a browser, so is not limited by CORS policy.

I have tried many 'Access-Control-Allow-xxx' headers but none of them worked..

These headers must be returned by the server in response - in your case by the imgur server. You can't set them in the request from browser, so it'll never work.

Error: Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field Access-Control-Allow-Headers is not allowed by Access-Control-Allow-Headers in preflight response.

Possible solutions to your problem:

  1. If you have access to the backend api you can set the "Access-Control-Allow-Origin" header on the server and let your app access the api - but as you won't have access to the imgur server - you probably can't do that.

  2. Disable CORS in the browser - you can use a plugin like: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en. This workaound should be fine for development. The plugin will disable your CORS settings and you will be able to hit imgur apis.

  3. The third solution is using a proxy. You can setup a small node server using express. You will then hit your own node server, which in turn will hit the imgur api. As node server is not a browser environment, it won't have any CORS issue and you will be able to access imgur API that way. This is also the reason you were able to hit the API from Postman without any issues. As Postman is not a browser environment, it's not limited by CORS policy.

Jatin Gupta
  • 889
  • 6
  • 15
  • 1
    The link for the chrome store doesn't work, but I found another extension that did the trick. Thanks good sir you deserve the up-vote!!! – Rodrigo Feb 05 '20 at 00:36
  • 1
    Let's say I use the extension and disable it locally. What happens when I deploy it? Will users not be blocked by cors thing? – Cristian G Mar 06 '21 at 09:43
  • @ChristianG That is correct, therefor the browser extensions aren't useful for mass deployment tools. – FreeSoftwareServers Jul 14 '22 at 04:27
  • The point 3 is very important and useful here. Using an express to create a simple server means you will then make the calls from your browser to that server, that server then calls the external server, thus getting around the CORS security – artworkjpm Jan 29 '23 at 13:51
8

That's because Access-Control-Allow-Headers, Access-Control-Allow-Methods are the headers that is used by the server. The server appends the header by a middleware.

Now, imagine in the server(in this below example an express server) with CORS enabled this kind of (default) headers are getting set:

app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With, Accept');
});

And you are sending Access-Control-Allow-Headers from the client side, and server sees that as a header that is not whitelisted.

So, in headers just use these:

headers: {
    'content-type': 'application/json',
    'Authorization': 'Client-ID [my-client-id]'
}

It should work fine.

Btw, I think it is working with postman because:

  • Postman cannot set certain headers if you don't install that tiny postman capture extension.
  • Browser security stops the cross origin requests. If you disable the chrome security it will do any CORS request just fine.

Also, according to this:

I believe this might likely be that Chrome does not support localhost to go through the Access-Control-Allow-Origin -- see Chrome issue

To have Chrome send Access-Control-Allow-Origin in the header, just alias your localhost in your /etc/hosts file to some other domain, like:

127.0.0.1   localhost yourdomain.com

Then if you'd access your script using yourdomain.com instead of localhost, the call should succeed.

Note: I don't think the content type should be application/json it should be like image/jpeg or something. Or maybe don't include that header if it doesn't work.

Aritra Chakraborty
  • 12,123
  • 3
  • 26
  • 35
  • 1
    I have used `application/json` in Postman and it worked, I don't think there's the problem. I have removed additional headers and left only `content-type` and `authorization` and this is the errror message I get: Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. – Cristian G Dec 08 '18 at 17:03
  • Okay, how about adding this to your header `'Access-Control-Allow-Headers': 'Content-Type, Authorization'` – Aritra Chakraborty Dec 08 '18 at 17:06
  • Now I am back to the original error message `Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field Access-Control-Allow-Headers is not allowed by Access-Control-Allow-Headers in preflight response`. – Cristian G Dec 08 '18 at 17:08
  • I see from the CURL request to their API you only need auth. And optionally a content type. `curl --request POST \ --url https://api.imgur.com/3/image \ --header 'Authorization: Client-ID {{clientId}}' \ --header 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \ --form image=R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7` – Aritra Chakraborty Dec 08 '18 at 17:14
  • Edited the answer, it seems chrome/ff doesn't support localhost for cors – Aritra Chakraborty Dec 08 '18 at 17:20
  • I added `127.0.0.1 localhost ttttestdomain.com` to my hosts file (everything else in it is commented) and added the `'Access-Control-Allow-Origin': '*'` header but it throws the same error and I can see that it is not using the alias, it still says `Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy..` – Cristian G Dec 08 '18 at 17:38
  • 1
    yeah but are you opening `ttttestdomain.com:3000` and testing from there? or still using `localhost:3000`? – Aritra Chakraborty Dec 08 '18 at 17:42
  • You are right, I was doing it wrong. Opened it from `ttttestdomain.com:3000` but it throws `Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://ttttestdomain.com:3000' has been blocked by CORS policy: Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers in preflight response.` :( – Cristian G Dec 08 '18 at 17:46
  • I give up, I will use firebase storage to store the photos and get over with. Thank you ever so much for you help and time!! – Cristian G Dec 08 '18 at 18:33
  • One thing, can you just use `axios` instead of `node-fetch`? Also, you can try type: jsonp (before giving up i mean) – Aritra Chakraborty Dec 08 '18 at 18:38
1

I have some few observations in my own app that helped me solve this issue. I have a node app as a backend api service and a VueJS built front end. I set my node app with cors with a list of endpoints that are allowed to access my node app. Working on my local machine doesn't give me any errors until I upload it to my server.

here are my environments Local Environment

  • Nodejs: 12.16.1
  • OS: Windows 10
  • DB: MySQL
  • NodeJS Server Framework: ExpressJS
  • Upload Module: Multer

Production Environment

  • Nodejs: 12.19.0
  • OS: Ubuntu 20.0.4
  • DB: MySQL
  • NodeJS Server Framework: ExpressJS
  • Upload Module: Multer
  • nginx

Here are my observations based on my production built app.

  1. When I upload a form with image [500kb and above] [post or put], the cors error shows up but less than that, it all went fine.
  2. If I use form data to send data to the server, I see 2 requests in my network tab, the OPTIONS and the actual request.
  3. The actual request failed but I saw that my content-length is very high which leads me to the conclusion that my request is rejected due to the large amount of data that the client sent which my server may have limited. I know that may be misleading but the solution I did works so I don't know why cors issue is popping up even though the data limit is the issue.

MY SOLUTION: In my nginx config file, I increased my client_max_body_size to 100M. I believe that nginx has a default of 1MB

  1. Open /etc/nginx/sites-available/your-server-file where your-server-file can be like www.example.com or default.
  2. Add the following line inside the server block. You can set it to any amount you want other than 100M.
server {
    client_max_body_size 100M;
    ...
}

  1. type in sudo systemctl restart nginx to restart nginx.
  2. type in sudo nginx -t to check if change is successful.
  3. Reload your app if you are using pm2 and you are done.
1

According to this article I used this command in linux and SOME OF(!) cross-origins fixed.

google-chrome --disable-web-security --user-data-dir="/tmp/YOUR_TEMPORARY_PATH"

Mohammad Reza
  • 693
  • 9
  • 16
0

This will not work if you pass headers from frontend. CORS policy is enabled by browsers. Browser blocks the response when they don't found the headers in response.

Possible Solutions:

  1. You can pass the headers in response (If you have the access of backend or ask the API provider for this)

  2. You can setup a middleware to resolve this.

You can get information from here