laravel / valet

A more enjoyable local development experience for Mac.
https://laravel.com/docs/valet
MIT License
2.52k stars 699 forks source link

how to enable cors for dev website that ONLY serves static assets #377

Closed vesper8 closed 6 years ago

vesper8 commented 7 years ago

I know that "CORS is usually handled at the app level" however I have a scenario here where I want to load css/js from a dev domain that is served by valet. However the domain "https//assets.mydomain.dev" ONLY has css/js/images and does not implement any kind of php where I would normally be setting CORS rules.

The only way to achieve this that I can think of is to add CORS whitelisting in the nginx configuration for that specific website (although I would be happy to categorically whitelist everything for cors since this is a dev environment)

Any way to achieve this with Valet?

vesper8 commented 7 years ago

This is driving me mad.. been trying to fix it for hours

I tried the solution in this gist https://gist.github.com/milesw/832ffe73c1e9ff3af81f5148f8e0ee45

<?php

class StaticCorsValetDriver extends BasicValetDriver
{
    /**
     * Determine if the driver serves the request.
     *
     * @param  string $sitePath
     * @param  string $siteName
     * @param  string $uri
     * @return bool
     */
    public function serves($sitePath, $siteName, $uri)
    {
        if (file_exists($sitePath . '/index.php') && file_exists($sitePath . '/cdn_mirror.php')) {
            if (0) {
                echo "<pre>" . print_r($sitePath, true) . "</pre>";
                echo "<pre>" . print_r($siteName, true) . "</pre>";
                echo "<pre>" . print_r($uri, true) . "</pre>";
                var_dump($this->isStaticFile($sitePath, $siteName, $uri));
                die();
            }
            return $this->isStaticFile($sitePath, $siteName, $uri);
        }
    }

    /**
     * Determine if the incoming request is for a static file.
     *
     * @param  string $sitePath
     * @param  string $siteName
     * @param  string $uri
     * @return string|false
     */
    public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri)
    {
        //die("123");
        if (1) {
            header('Access-Control-Allow-Origin: *');
        } else {
            if (isset($_SERVER['HTTP_ORIGIN'])) {
                //header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
                header("Access-Control-Allow-Origin: *");
                header('Access-Control-Allow-Credentials: true');
                header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
            }
            if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
                if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
                    header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
                if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
                    header("Access-Control-Allow-Headers:{$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

                exit(0);
            }
        }

        parent::serveStaticFile($staticFilePath, $sitePath, $siteName, $uri);
    }
}

I can confirm that my requests to the static files (fonts) are going through my custom driver.. yet the CORS is not being applied.

It's not being applied in Chrome, but also not showing when I use the command line and do a curl -I

The files are reachable and if I point my browser to the full url (of the font files) then it does immediately download without issue.

Since all of these sites use the valet secure flag, which generates a nginx configuration for each of those sites, I have also tried adding the CORS inside the nginx configs. But that too is not helping at all

I tried adding

    location / {

        if ($request_uri ~* ^.*?\.(eot|ttf|woff|woff2)$) {
            add_header Access-Control-Allow-Origin *;
        }

        rewrite ^ /Users/vesperknight/.composer/vendor/laravel/valet/server.php last;
    }

Would really appreciate some hint

adamwathan commented 7 years ago

If it just serves static assets why do you need CORS? Are you making a POST request to a CSS file?

Static files aren't served by Valet directly, it uses the driver to check if a file is static and if so uses X-Accel-Redirect delegate serving that file back to nginx.

On Mon, May 15, 2017 at 5:05 AM vesper8 notifications@github.com wrote:

This is driving me mad.. been trying to fix it for hours

I tried the solution in this gist https://gist.github.com/milesw/832ffe73c1e9ff3af81f5148f8e0ee45

<?php

class StaticCorsValetDriver extends BasicValetDriver { /**

  • Determine if the driver serves the request.
  • @param string $sitePath
  • @param string $siteName
  • @param string $uri
  • @return bool */ public function serves($sitePath, $siteName, $uri) { if (file_exists($sitePath . '/index.php') && file_exists($sitePath . '/cdn_mirror.php')) { if (0) { echo "

    " . print_r($sitePath, true) . "
    "; echo "
    " . print_r($siteName, true) . "
    "; echo "
    " . print_r($uri, true) . "
    "; var_dump($this->isStaticFile($sitePath, $siteName, $uri)); die(); } return $this->isStaticFile($sitePath, $siteName, $uri); } }

    /**

  • Determine if the incoming request is for a static file.
  • @param string $sitePath
  • @param string $siteName
  • @param string $uri
  • @return string|false / public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri) { //die("123"); if (1) { header('Access-Control-Allow-Origin: '); } else { if (isset($_SERVER['HTTP_ORIGIN'])) { //header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); header("Access-Control-Allow-Origin: *"); header('Access-Control-Allow-Credentials: true'); header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); } if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers:{$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

            exit(0);
        }
    }
    
    parent::serveStaticFile($staticFilePath, $sitePath, $siteName, $uri);

    } }

I can confirm that my requests to the static files (fonts) are going through my custom driver.. yet the CORS is not being applied.

It's not being applied in Chrome, but also not showing when I use the command line and do a curl -I

The files are reachable and if I point my browser to the full url (of the font files) then it does immediately download without issue.

Since all of these sites use the valet secure flag, which generates a nginx configuration for each of those sites, I have also tried adding the CORS inside the nginx configs. But that too is not helping at all

I tried adding

location / {

    if ($request_uri ~* ^.*?\.(eot|ttf|woff|woff2)$) {
        add_header Access-Control-Allow-Origin *;
    }

    rewrite ^ /Users/vesperknight/.composer/vendor/laravel/valet/server.php last;
}

Would really appreciate some hint

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/laravel/valet/issues/377#issuecomment-301418430, or mute the thread https://github.com/notifications/unsubscribe-auth/AEH3bEDrqiq_g2YTFZuQxQBxc0GqM2k-ks5r6BVcgaJpZM4Narnw .

adamwathan commented 7 years ago

The gist does look like it should work though, do OPTIONS requests hit that function?

vesper8 commented 7 years ago

I have a script that mirrors CDNs such as maxcdn.bootstrapcdn.com and cdnjs.cloudflare.com so that I can use those assets completely offline simply by changing the .com to .dev

Many of these files are css files that have @font-face entries that link to fonts (eot,ttf,woff)

These files produce a CORS error.. it's always done that.. had plenty of problems with that in the past but adding cors directives to the Nginx config has always solved it easily enough.

As I described though with this case no matter how much I try to force cors to be on, it just won't stick

I'm not sure what you mean by X-Accel-Redirect but could this be interfering?

adamwathan commented 7 years ago

Ah interesting. I feel like that gist should be the right sort of solution but I wonder if OPTIONS requests are even hitting it at all.

If every single request to that project is a static file, try hardcoding return true in isStaticFile and see if that helps drive OPTIONS requests to the serveStaticFile method.

drbyte commented 7 years ago

When doing asset "sharding" like this, is there any need to set something on the "other"/"calling" domain?

vesper8 commented 7 years ago

hardcoding 'return true' did not change anything =/

Still getting those cors errors in the browser and still not seeing the header in the console:

curl -I https://maxcdn.bootstrapcdn.dev/bootstrap/3.3.7/css/bootstrap.min.css
HTTP/1.1 200 OK
Server: nginx/1.12.0
Date: Wed, 17 May 2017 04:51:16 GMT
Content-Type: text/css
Content-Length: 121200
Last-Modified: Mon, 15 May 2017 06:25:41 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "591949e5-1d970"
Accept-Ranges: bytes

curl -I https://maxcdn.bootstrapcdn.dev/font-awesome/4.7.0/fonts/fontawesome-webfont.woff
HTTP/1.1 200 OK
Server: nginx/1.12.0
Date: Wed, 17 May 2017 04:51:37 GMT
Content-Type: application/font-woff
Content-Length: 98024
Last-Modified: Mon, 15 May 2017 06:45:48 GMT
Connection: keep-alive
ETag: "59194e9c-17ee8"
Accept-Ranges: bytes

I found a "workaround" fix which is probably terrible but on OSX you can launch Chrome with disabled security which makes all those cors errors go away

open -a Google\ Chrome --args --disable-web-security --user-data-dir
BSN4 commented 7 years ago

@vesper8 I had this issue today and I discovered a workaround .. on nginx/valet/valet.conf

    location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
        internal;

    #add this line
        add_header Access-Control-Allow-Origin *;

        alias /;
        try_files $uri $uri/;
    }
vesper8 commented 7 years ago

@vesper8 I had this issue today and I discovered a workaround it'a abit slow but It did the job for local dev .. replace ValetDriver.php with this https://gist.github.com/phpfalcon/d11feca66fe96d1262bc195209ded8e0

Thanks a lot for sharing @phpfalcon !!

I tried this, replacing the ValetDriver.php in ~/.composer/vendor/laravel/valet/cli/drivers/

and wow it works!! I tried sooo many things to get the CORS issues to work and nothing worked but this does work! Unfortunately I can't really understand why it works!

However I still have a problem with my fonts not fully working (I am hosting a mirror of common CDNs on my own dev).

See these errrors:

Failed to decode downloaded font: https://develop.omitted.dev/fonts/komika/KomikaAxis.woff
home#/search:1 OTS parsing error: incorrect file size in WOFF header
home#/search:1 Failed to decode downloaded font: https://fonts.googleapis.dev/fonts/opensans/v14/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://fonts.googleapis.dev/fonts/opensans/v14/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://maxcdn.bootstrapcdn.dev/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://fonts.googleapis.dev/fonts/opensans/v14/DXI1ORHCpsQm3Vp6mXoaTRampu5_7CjHW5spxoeN3Vs.woff2
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://fonts.googleapis.dev/fonts/opensans/v14/DXI1ORHCpsQm3Vp6mXoaTRampu5_7CjHW5spxoeN3Vs.woff2
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://fonts.googleapis.dev/fonts/opensans/v14/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://fonts.googleapis.dev/fonts/opensans/v14/MTP_ySUJH_bn48VBG8sNShampu5_7CjHW5spxoeN3Vs.woff2
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://develop.omitted.dev/fonts/komika/KomikaAxis.woff
home#/search:1 OTS parsing error: incorrect file size in WOFF header
home#/search:1 Failed to decode downloaded font: https://maxcdn.bootstrapcdn.dev/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0
home#/search:1 OTS parsing error: Failed to convert WOFF 2.0 font to SFNT
home#/search:1 Failed to decode downloaded font: https://maxcdn.bootstrapcdn.dev/font-awesome/4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0
home#/search:1 OTS parsing error: incorrect file size in WOFF header

I thought maybe it was because your ValetDriver.php didn't have any mime-types specified for fonts so I added this:

                // fonts
                'svg'   => "image/svg+xml",
                'ttf'   => "application/x-font-ttf",
                //'ttf'   => "application/x-font-truetype",
                'otf'   => "application/x-font-opentype",
                'woff'  => "application/font-woff",
                'woff2' => "application/font-woff2",
                'eot'   => "application/vnd.ms-fontobject",
                'sfnt'  => "application/font-sfnt",

and reloaded nginx.. but still it doesn't work... any ideas?

BSN4 commented 7 years ago

I edited my post since i played with this file you can do it in nginx side

BSN4 commented 7 years ago

 location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
        internal;

    #add this line
        add_header Access-Control-Allow-Origin *;

        alias /;
        try_files $uri $uri/;
    }

On valet.conf

BSN4 commented 7 years ago

you are using ssl incase you choose valetdriver method you want to modify the file to work with https or just try the nginx method but you should revert back old valetdriver file first

vesper8 commented 7 years ago

I have had no luck getting my CORS woes to go away using your nginx trick.. however the gist you had initially shared which is no longer available had worked.

Here is what's inside my ValetDriver.php which makes all my CORS issues go away

    public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri)
    {
        /**
         * Back story...
         *
         * PHP docs *claim* you can set default_mimetype = "" to disable the default
         * Content-Type header. This works in PHP 7+, but in PHP 5.* it sends an
         * *empty* Content-Type header, which is significantly different than
         * sending *no* Content-Type header.
         *
         * However, if you explicitly set a Content-Type header, then explicitly
         * remove that Content-Type header, PHP seems to not re-add the default.
         *
         * I have a hard time believing this is by design and not coincidence.
         *
         * Burn. it. all.
         */
        //header('Content-Type: text/html');
        //header_remove('Content-Type');
        header('Content-type: ' . $this->detectFileMimeType($staticFilePath));

        header('X-Accel-Redirect: /' . VALET_STATIC_PREFIX . $staticFilePath);
    }

    public function detectFileMimeType($filename='')
    {
        $mime_types = array(
                'css' => 'text/css',
                'js' => 'application/javascript',
                'json' => 'application/json',
                'xml' => 'application/xml',
                'swf' => 'application/x-shockwave-flash',
                'flv' => 'video/x-flv',

                // images
                'png' => 'image/png',
                'jpe' => 'image/jpeg',
                'jpeg' => 'image/jpeg',
                'jpg' => 'image/jpeg',
                'gif' => 'image/gif',
                'bmp' => 'image/bmp',
                'ico' => 'image/vnd.microsoft.icon',
                'tiff' => 'image/tiff',
                'tif' => 'image/tiff',
                'svg' => 'image/svg+xml',
                'svgz' => 'image/svg+xml',

                // archives
                'zip' => 'application/zip',
                'rar' => 'application/x-rar-compressed',
                'exe' => 'application/x-msdownload',
                'msi' => 'application/x-msdownload',
                'cab' => 'application/vnd.ms-cab-compressed',

                // audio/video
                'mp3' => 'audio/mpeg',
                'qt' => 'video/quicktime',
                'mov' => 'video/quicktime',

                // adobe
                'pdf' => 'application/pdf',
                'psd' => 'image/vnd.adobe.photoshop',
                'ai' => 'application/postscript',
                'eps' => 'application/postscript',
                'ps' => 'application/postscript',

                // ms office
                'doc' => 'application/msword',
                'rtf' => 'application/rtf',
                'xls' => 'application/vnd.ms-excel',
                'ppt' => 'application/vnd.ms-powerpoint',

                // open office
                'odt' => 'application/vnd.oasis.opendocument.text',
                'ods' => 'application/vnd.oasis.opendocument.spreadsheet',

                // fonts
                'svg'   => "image/svg+xml",
                'ttf'   => "application/x-font-ttf",
                //'ttf'   => "application/x-font-truetype",
                'otf'   => "application/x-font-opentype",
                'woff'  => "application/font-woff",
                'woff2' => "application/font-woff2",
                'eot'   => "application/vnd.ms-fontobject",
                'sfnt'  => "application/font-sfnt",
            );

            @$ext = strtolower(array_pop(explode('.',$filename)));
            if (array_key_exists($ext, $mime_types)) {
                return $mime_types[$ext];
            }
            elseif (function_exists('finfo_open')) {
                $finfo = finfo_open(FILEINFO_MIME);
                $mimetype = finfo_file($finfo, $filename);
                finfo_close($finfo);
                return $mimetype;
            }
            else {
                return 'application/octet-stream';
            }
    }
poul-kg commented 7 years ago

Here is the Gist for enabling CORS for every request in Laravel Valet.

https://gist.github.com/poul-kg/b669a76fc27afcc31012aa0b0e34f738

I got an idea from @if4lcon response and extended it to work for my purposes. If you're sending some headers not mentioned in this line in gist: add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Authorization'; - Just add them after comma and execute

valet restart

vwasteels commented 6 years ago

I tried @poul-kg solution, and it worked !! :)