craftcms / cms

Build bespoke content experiences with Craft.
https://craftcms.com
Other
3.28k stars 635 forks source link

ProxyPass configuration issue with image transforms #6235

Closed NikosDevPhp closed 4 years ago

NikosDevPhp commented 4 years ago

Description

I am trying to set up a multi site blog at https://mysite.com/blog/ and https://en.mysite.com/blog/ Our Craft .env file uses as a default_site_url= https://mysite.com/blog/ and for english_site respectively So we have an Apache ProxyPass /blog IP that passes all /blog requests to our main site to the IP of Craft Server. In my web/index.php i can check http_host and define('CRAFT_SITE') variable accordingly. When you ProxyPass the standard html request is proxypassed but all the assets, ajax calls in a page load are not so I have created an alias to return the assetBaseUrl according to the host of the request which is working ok for assets volumes even minifier plugin. The issue is that we use image transforms. After debugging the code I saw that when image.getUrl() is used for an asset to create it's resource (url) for the first time renders a img src of sth like https://www.mysite.com/index.php?p=actions/assets/generate-transform&transformId=4 where the correct should be https://www.mysite.gr/blog/index.php?p=actions/assets/generate-transform&transformId=4 What I can see is it takes the default_site_url https://mysite.com/blog/ and then takes the static::host which takes a substr of only the domain name thus making a request to my original site and not craft. After I go to admin and just refresh the admin panel (e.g. go to another tab) i get a generating transforms menu appearing and disappearing and when i refresh the frontend the image is rendered correctly. So probably different code is run on CP that works fine. Shouldn't the url generated to create image transform comply with the .env DEFAULT_SITE_URL url?

Steps to reproduce

  1. Set up site url of www.mysite.com/something with mysite.com being actually a different domain
  2. Use a ProxyPass for all requests to "/something"
  3. The url rendered point to the original mysite.com and not the craft instance

Additional info

NikosDevPhp commented 4 years ago

Same from php from my own module: Article is an entry $article->url takes into account exact SITE_URL from .env $article->featuredImage->one()->getUrl('teaserBig') does not

brandonkelly commented 4 years ago

Where is the actual index.php file located on the Craft server, in relation to the host’s webroot?

NikosDevPhp commented 4 years ago

The apache DocumentRoot is the /web folder(which contains the index.php)

NikosDevPhp commented 4 years ago

@brandonkelly The issue is that I am using a ProxyPass which sets an X-Forwarded-For header. i.e. the {{ craft.app.request.absoluteUrl }} returns from vendor/yiisoft/yii2/web/Request.php::736

elseif ($this->headers->has('X-Forwarded-Host')) {
                $this->_hostInfo = $http . '://' . trim(explode(',', $this->headers->get('X-Forwarded-Host'))[0]);

which sets $hostInfo to 'https://mysite.com" , the original host of the request without the '/blog' part which makes it render the wrong url. Can I fix it somehow with a general/config setting? As I said the default_site_url in my .env and everywhere else is https://mysite.com/blog/, I don't use the SERVER IP anywhere in the code

brandonkelly commented 4 years ago

What if you were to place index.php within a blog/ folder within your web root? (so web/blog/index.php)

You can still keep the rest of your project located above the web root. Just change this line in index.php:

define('CRAFT_BASE_PATH', dirname(__DIR__));

to:

define('CRAFT_BASE_PATH', dirname(__DIR__, 2));
NikosDevPhp commented 4 years ago

I did something similar created a directory /blog and then I had issues so I created a symbolic link from /web/blog to /web and and now Craft knows that https://mysite.com/blog/ is mysite's url

Thanks a lot @brandonkelly !!! You're the best

Still think the default_site_url .env variable should be respected when generating urls everywhere in Craft. I saw somewhere static method ::host when resolving the host name, I think that was taking a substr and stripping '/blog' from the url. I think I should be able by providing the default_site_url tell craft that this is the DocumentRoot of my website

brandonkelly commented 4 years ago

Glad you go it sorted!

I think I should be able by providing the default_site_url tell craft that this is the DocumentRoot of my website

@NikosDevPhp that’s not a safe assumption because in most cases if the base site URL is something like /blog, the web server’s document root is still up a level higher.

NikosDevPhp commented 4 years ago

@brandonkelly Sorry to bother you again.

When Images transforms are created for the frotnend the urls are wrong. I have to refresh the CP page to trigger GeneratePendingTransforms.

When any action Url (in my case image transform) is to be rendered the baseUrl is created by the host + the script name (hostInfo() called which strips any default_site_url ENV variable)

vendor/craftcms/cms/src/helpers/UrlHelper.php

// If we must show the script name, then just start with the script URL,
        // regardless of whether this is a CP or site request, as we can't assume
        // that index.php lives within the base URL anymore.
        if ($showScriptName) {
            if ($request->getIsConsoleRequest()) {
                // No way to know for sure, so just guess
                $baseUrl = '/index.php';
            } else {
                $baseUrl = static::host() . $request->getScriptUrl();
            }

On the other side when GeneratePendingTransforms job runs the urls generated are directly from default_site_url .env file.

In a proxy pass situation: Now Regardless of where I place my DocumentRoot the static::host will always keep the Origin Domain Server that forwarded did the Proxying so as to generate urls that point to the origin server.

There is clearly something not taken into account here!

brandonkelly commented 4 years ago

@NikosDevPhp probably best if you just write into support@craftcms.com about this, and we can try to help you from there.