Andrew Allen
Andrew Allen

Reputation: 8002

Why do I get a CORS error when I've set Access-Control-Allow-Origin?

Problem

The Access-Control-Allow-Origin of the response header matches the request Origin header but I'm still getting the error Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://myappname.herokuapp.com/api/v1/products. (Reason: CORS request did not succeed).[Learn More]

Headers

Response Header

HTTP/1.1 308 PERMANENT REDIRECT
Connection: keep-alive
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin: http://localhost
Content-Length: 311
Content-Type: text/html; charset=utf-8
Date: Thu, 04 Apr 2019 10:03:39 GMT
Location: http://mpappname.herokuapp.com/api/v1/products/
Server: waitress
Via: 1.1 vegur

Request Header

Host: mpappname.herokuapp.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,content-type
Referer: http://localhost/admin/main/products/create
Origin: http://localhost
DNT: 1
Connection: keep-alive

Background

The code that generates the headers is below but I'm more concerned with understanding why CORS preflight is rejecting this. This is for a flask api with a products CRUD design and token based authentication.

Cut version

from flask_cors import CORS

def create_app(config_name):
    ...
    CORS(app, origins="http://localhost",
      allow_headers=["Content-Type", "Authorization", "Access-Control-Allow-Credentials"],
      supports_credentials=True)
    ...
    return app

Full version

from flask import Flask
from config import config
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
db = SQLAlchemy()

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)
    CORS(app, origins="http://localhost",
      allow_headers=["Content-Type", "Authorization", "Access-Control-Allow-Credentials"],
      supports_credentials=True)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    from .api import api as api_blueprint
    app.register_blueprint(api_blueprint, url_prefix='/api/v1')

    return app

Upvotes: 4

Views: 7467

Answers (1)

Andrew Allen
Andrew Allen

Reputation: 8002

I was requesting

http://mpappname.herokuapp.com/api/v1/products

instead of

http://mpappname.herokuapp.com/api/v1/products/

Maybe obvious to some but I needed to read this blog post explicitly to understand this:

https://airbrake.io/blog/http-errors/308-permanent-redirect

The appearance of a 308 Permanent Redirect is usually not something that requires much user intervention. All modern browsers will automatically detect the 308 Permanent Redirect response code and process the redirection action to the new URI automatically. The server sending a 308 code will also include a special Location header as part of the response it sends to the client. This Location header indicates the new URI where the requested resource can be found. For example, if an HTTP POST method request is sent by the client as an attempt to login at the https://airbrake.io URL, the web server may be configured to redirect this POST request to a different URI, such as https://airbrake.io/login. In this scenario, the server may respond with a 308 Permanent Redirect code and include the Location: https://airbrake.io/login header in the response. This informs the user agent (browser) that the POST request data (login info) was received by the server, but the resource has been permanently moved to the Location header URI of https://airbrake.io/login.

Upvotes: 15

Related Questions