fruitcake / laravel-cors

Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application
MIT License
6.27k stars 616 forks source link

Cors not working with POST request with Docker and Nginx #543

Open stackerito opened 3 years ago

stackerito commented 3 years ago

I am using Docker + Nginx to proxy requests between my frontend React project on localhost to backend Laravel API on localhost:8080.

I try to register a new user from the frontend on localhost/register to Laravel localhost:8080/api/register

The GET request to sanctum/csrf-cookie works and it sets the XSRF-TOKEN

But then I try to send POST request to register a user and I get CORS error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/api/register. (Reason: CORS header ‘Access-Control-Allow-Origin’ is missing)

And despite this message, the user is registered successfully. But the error still appears because indeed the response does not contain the ‘Access-Control-Allow-Origin’ header.

I'm not sure if something in the middle (nginx, docker) is blocking it, but the GET request clearly works because when I try to change the allowed_origins in my cors.php file it throws an error.

This is my cors.php:

    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['http://localhost'],

    'allowed_origins_patterns' => ['*'],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

This is the OPTIONS response headers:

Status 204 No Content
Version HTTP/1.1
Transferred 494 B (0 B size)
Referrer Policy strict-origin-when-cross-origin

    HTTP/1.1 204 No Content

    Server: nginx/1.20.1

    Content-Type: text/html; charset=UTF-8

    Connection: keep-alive

    X-Powered-By: PHP/8.0.10

    Cache-Control: no-cache, private

    Date: Fri, 10 Sep 2021 11:03:28 GMT

    Access-Control-Allow-Origin: http://localhost

    Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers

    Access-Control-Allow-Credentials: true

    Access-Control-Allow-Methods: POST

    Access-Control-Allow-Headers: content-type,x-xsrf-token

    Access-Control-Max-Age: 0

This is the OPTIONS request headers:

Status 204 No Content
Version HTTP/1.1
Transferred 494 B (0 B size)
Referrer Policy strict-origin-when-cross-origin

    Access-Control-Allow-Credentials
        true
    Access-Control-Allow-Headers
        content-type,x-xsrf-token
    Access-Control-Allow-Methods
        POST
    Access-Control-Allow-Origin
        http://localhost
    Access-Control-Max-Age
        0
    Cache-Control
        no-cache, private
    Connection
        keep-alive
    Content-Type
        text/html; charset=UTF-8
    Date
        Fri, 10 Sep 2021 11:07:14 GMT
    Server
        nginx/1.20.1
    Vary
        Origin, Access-Control-Request-Method, Access-Control-Request-Headers
    X-Powered-By
        PHP/8.0.10

    OPTIONS /api/register HTTP/1.1

    Host: localhost:8080

    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0

    Accept: */*

    Accept-Language: en-US,en;q=0.5

    Accept-Encoding: gzip, deflate

    Access-Control-Request-Method: POST

    Access-Control-Request-Headers: content-type,x-xsrf-token

    Referer: http://localhost/

    Origin: http://localhost

    Connection: keep-alive

    Sec-Fetch-Dest: empty

    Sec-Fetch-Mode: cors

    Sec-Fetch-Site: cross-site

This is the POST response headers:

Status 200 OK
Version HTTP/1.1
Transferred 16.68 KB (16.49 KB size)
Referrer Policy strict-origin-when-cross-origin

    HTTP/1.1 200 OK

    Server: nginx/1.20.1

    Date: Fri, 10 Sep 2021 11:09:32 GMT

    Content-Type: text/html; charset=UTF-8

    Transfer-Encoding: chunked

    Connection: keep-alive

    X-Powered-By: PHP/8.0.10

    Accept
        application/json
    Accept-Encoding
        gzip, deflate
    Accept-Language
        en-US,en;q=0.5
    Connection
        keep-alive
    Content-Length
        62
    Content-Type
        application/json
    Cookie
        XSRF-TOKEN=long-value-here
    Host
        localhost:8080
    Origin
        http://localhost
    Referer
        http://localhost/
    Sec-Fetch-Dest
        empty
    Sec-Fetch-Mode
        cors
    Sec-Fetch-Site
        cross-site
    User-Agent
        Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0
    X-XSRF-TOKEN long-value-here

This is the POST request headers:

Status 200 OK
Version HTTP/1.1
Transferred 16.68 KB (16.49 KB size)
Referrer Policy strict-origin-when-cross-origin

    HTTP/1.1 200 OK

    Server: nginx/1.20.1

    Date: Fri, 10 Sep 2021 11:09:32 GMT

    Content-Type: text/html; charset=UTF-8

    Transfer-Encoding: chunked

    Connection: keep-alive

    X-Powered-By: PHP/8.0.10

    POST /api/register HTTP/1.1

    Host: localhost:8080

    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0

    Accept: application/json

    Accept-Language: en-US,en;q=0.5

    Accept-Encoding: gzip, deflate

    Content-Type: application/json

    X-XSRF-TOKEN: long-value-here

    Content-Length: 62

    Origin: http://localhost

    Connection: keep-alive

    Referer: http://localhost/

    Cookie: XSRF-TOKEN=long-value-here

    Sec-Fetch-Dest: empty

    Sec-Fetch-Mode: cors

    Sec-Fetch-Site: cross-site

In order to fix this CORS error I added the following to my nginx config:

        set $METHODS  'GET, POST, OPTIONS, HEAD';
        set $HEADERS  'Authorization, Origin, X-Requested-With, Content-Type, Accept';

        if ( $request_method = POST ){
          add_header 'Access-Control-Allow-Origin' 'http://localhost';
          add_header 'Access-Control-Allow-Credentials' 'true';
        }

    }

So now my nginx config file looks like this:

server {
    listen 8080;
    index index.php index.html;    
    error_log /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html/public;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {        
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

        set $METHODS  'GET, POST, OPTIONS, HEAD';
        set $HEADERS  'Authorization, Origin, X-Requested-With, Content-Type, Accept';

        if ( $request_method = POST ){
          add_header 'Access-Control-Allow-Origin' 'http://localhost';
          add_header 'Access-Control-Allow-Credentials' 'true';
        }

    }

}

  server {    
    listen       80;       
      location / {      
      proxy_pass      http://node:3000;

    }
  }

So manually telling nginx to send back these headers for the POST request work, but why I don't get this issue for GET and OPTIONS?

apteles commented 3 years ago

I've been facing the same, problem.

jesseyvanofferen commented 2 years ago

I'm getting the same error. I have a POST request that does seem to be processed but still receives a CORS error in the browser.

christianschoenmakers commented 2 years ago

I'd suggest to read this post about how Laravel is working with a reverse proxy: https://medium.com/weekly-webtips/laravel-and-cors-behind-a-reverse-proxy-b737566bdd96

If you're using Laravel 9, you should change the $proxies variable to '*' in your App/Http/Middleware/TrustProxies.php file