Problem/Motivation

Currently it is possible to enforce a $base_url from within settings.php. Up until Drupal 4.6.x this was required. Since then it was only necessary on broken webserver configurations. Also depending on the webserver configuration, setting $base_url was required in order to prevent instances of #2221699: HTTP_HOST header cannot be trusted (e.g., possibly #1355344: Possible to deface using HTTP_HOST if base_url not set in settings.php), but that issue has been resolved by introducing the trusted_host_patterns setting.

However, since there is no way to enforce the base URL in a Symfony request, the setting only works for code which is still depending on the deprecated globals. E.g., on a site which enforces $base_url from settings.php, modern code using something like $request->getSchemeAndHttpHost() will produce the wrong results in such configurations.

Note that enforcing $base_url in settings.php also influences $base_root and $base_path globals.

All of this functionality not covered at all by any tests.

Proposed resolution

Remove the option to specify a base_url from within settings.php.

Remaining tasks

Review.

User interface changes

None.

API changes

None.

Data model changes

None.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because it removes defect functionality
Issue priority Normal
Disruption Disruptive for existing sites if they are running on webservers with broken configuration
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

znerol’s picture

Status: Active » Needs review
Issue tags: +Quick fix, +Quickfix
FileSize
3.99 KB
znerol’s picture

Issue summary: View changes
znerol’s picture

Forgot the instance in Settings::initialize().

znerol’s picture

Issue summary: View changes
dawehner’s picture

Disruptive for existing sites if they are running on webservers with broken configuration

The question is whether we could, maybe in the CR, explain how to fix your webserver configuration.

Given that this feature fundamentally brakes assumptions about the request we have, we have problems, when we keep it.

aatdark’s picture

i'm using drupal behind a reverse proxy which handles HTTPS.
the connection to the actual web server is http.

Client --- [https] -- reverseproxy -- [http] -- webserver (drupal)

drupal8-beta14 works well except that the server sends redirects after POSTs to the http:// site (absolute path)

$settings['reverse_proxy'] = TRUE;
$settings['reverse_proxy_addresses'] = array('10.....
$base_url = 'https://....

what is the recommended "fix" in the [webserver]config?

Fabianx’s picture

Priority: Normal » Major
Status: Needs review » Reviewed & tested by the community
Issue tags: +rc deadline

This is major and a hard judgement call.

Tentatively setting to RTBC.

If we promise that base_url would do something, which it long time already does no longer that is a big problem ...

So removing is likely the best option.

It might need the #313145: Support X-Forwarded-* HTTP headers alternates in for it to work properly for all cases, which for now at least has a workaround as Request:: has statics.

catch’s picture

Status: Reviewed & tested by the community » Fixed

I've gone ahead and committed/pushed this to 8.0.x.

We know the feature is incompatible with Symfony's handling of base paths (and that the discrepancy between a Drupal base path and a Symfony base path is already a bad issue).

If this breaks an existing install, then that'll be a shame, but we can always revert the patch during a release candidate or even minor release.

However if we leave the feature in, we're stuck with it forever.

Thanks!

  • catch committed c2af04e on 8.0.x
    Issue #2528988 by znerol: Remove the option to specify a base_url from...
Fabianx’s picture

Published the change record. (which is how I had found that issue yesterday ;) )

chx’s picture

Status: Fixed » Active

So what are we doing to avoid Drupal running on an Apache bound to localhost behind a reverse proxy trying to load CSS/JS from http://localhost which is what $base_url was often used for?

catch’s picture

#313145: Support X-Forwarded-* HTTP headers alternates is supposed to add support for reverse proxies setting host.

The reason I committed this issue is because it's incompatible with the code that checks that - we need to not have three ways to get the same thing that actually get you different things.

pwolanin’s picture

Status: Active » Fixed

Adding note about file_public_base_url settinh to CR

hass’s picture

I'm not sure this was a good idea.

I had tons of users in linkchecker that used to run cron with localhost and sometimes with internal ports. In this case relative urls have been prefixed with these internal urls. The only solution was to configure the base path. How can these issues solved now? It does not require a reverse proxy to run into these issues.

hass’s picture

Status: Fixed » Active
lightsurge’s picture

I also think base_url in $settings is valid for some configurations...

With drupal, I often use an Apache2 vhost, coupled with a reverse proxy that is listening at a subdirectory.

So external_host.com/subfolder1 reverses to internal_host, where Drupal resides. That works pretty well, except during install: #2377233: $_SERVER['REQUEST_URI'] reverse proxies, base_urls and Apache2 don't seem to mix well although it's pretty easy to fix by altering REQUEST_URI in $settings along with setting base_url.

There are other people using this sort configuration, see end of #244593: Document how to configure Drupal for reverse proxies

lightsurge’s picture

I might be wrong but it doesn't look like #313145: Support X-Forwarded-* HTTP headers alternates adds support for reverse proxies being able to identify to the internal host the part of the uri which should be considered the base url (i.e. the subfolder that Drupal should consider the top level directory, and therefore not used as part of the Drupal path).

The alternative without base_url might either be setting up the internal host to remove that part of the request URI, or the reverse proxy to remove it from the request before it even reaches the internal web server, or maybe putting it in a subfolder just for the sake of keeping Drupal happy. Is this more valid? Not as easy.

I suppose after this patch, it might be possible to just set chagen REQUEST_URI in $settings, although I don't know if #313145: Support X-Forwarded-* HTTP headers alternates will impact on that.

gaele’s picture

+1 for #16. This is the situation:

Client --- [www.example.com/subdir] -- reverseproxy -- [internalhost.local] -- webserver (drupal)

So this statement:

This option was only necessary on broken webserver configurations.

is incorrect. Please change this in the change record.

znerol’s picture

Can anybody explain how you would host any non-drupal application with a setup like #16 or #18? In my opinion there is no value in supporting such setups if the only way to make them work is to retain Drupal specific settings which prevent us from moving forward towards a better architecture.

As pointed out in the issue summary, there is no such setting in Symfony and hence you wouldn't be able to host any Symfony based project like this.

gaele’s picture

Don't forget #14.
There is a need for this, people already use these kind of set ups, so we need a solution. Not necessarily using $base_url.

catch’s picture

drush has a --uri option, not sure why that won't work for #14.

We need to allow the setup, but we don't necessarily need explicit support for it in core - it could end up being a documentation issue.

hass’s picture

I never analyzed what drush does with --uri argument. So I need to ask how this works for my module. If this works reliable I'm fine with this solution. Linkchecker does all it's stuff inside cron. Maybe I need to change some code to make this happen?

Fabianx’s picture

#19: The problem is HTTP_HOST is only the host, but not the subdir.

e.g. you have external-host.com/subdir => but drupal is in internal-host.com, probably redirected based on varnish rules.

In the past you could just set $base_url to whatever you needed it to be and all generated urls would exhibit that behavior.

Additionally setting .htaccess RewriteBase to /subdir then all works.

So but Drupal now in 8 would generate: external-host.com, which is wrong as it never sees the subdir - but maybe it should, too.

Hmmm, I think it needs more testing the #16 / #18 might just work in practice according to Symfony using the original REQUEST_URI for that - unless Varnish does the rewriting and not .htaccess.

  • catch committed c2af04e on 8.1.x
    Issue #2528988 by znerol: Remove the option to specify a base_url from...
Patricia_W’s picture

I am testing version Drupal 8 of a site and as a result I am using a subdirectory to test. Now I have to add the directory name to all internal links.
I'm far from an expert in this area but if I add the directory name during testing I will have to remove it from the links when I change the URL. Is there a way to avoid this? Previously I could test using $Base_Url.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mat8iou’s picture

I'm wishing the $Base_Url setting was still there, as it solved a lot of problems in the past.

Having said that, I've worked out a (sort of) solution to the problem that may work for some people.

See my post on it & then the following post with the solution here:
https://www.drupal.org/node/2716043#comment-11139921

Fidelix’s picture

Unfortunately Drupal 8's inability to set a base_url prevents some of our projects from running on Platform.sh (#16).

toomanypets’s picture

Every site I've built with d6 and d7 has been located in subdirectory of webroot, using a top level .htaccess file and $base_url to mask the URL. For example:

webroot: /home/johndoe/sites/www.example.com/htdocs/
drupal: /home/johndoe/sites/www.example.com/htdocs/drupal7/
url: http://www.example.com

This is not a "broken webserver configuration." It's a very flexible and valid configuration.

I can't do this with d8. So I'll use d7 or backdrop instead.

Fabianx’s picture

#29: You should be able to just use the subdir functionality of the .htaccess rewrite rules to get that to work.

The problem here was not that we wanted to remove $base_url, but that by introducing Symfony we had partial support for $base_url and partially not, which would have led to even more bugs, because then your site configuration should have worked, but only for certain cases.

#28: Are you sure this is not resolvable with a .htaccess rewrite rule?

toomanypets’s picture

Regarding #29...

I was unable use the subdir functionality of the .htaccess rewrite rules to get this to work. Example:

RewriteRule ^$ d8/$1 [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ d8/$1 [L]

Problems

1. View source shows assets with subdirectory. For example:
<script src="/d8/core/assets/vendor/jquery/jquery.min.js?v=2.1.4"></script>

This should be:
<script src="/core/assets/vendor/jquery/jquery.min.js?v=2.1.4"></script>

2. Under Basic site settings (admin/config/system/site-information), the default front page prefix is:
http://www.example.com/d8

This should be:
http://www.example.com/

3. When adding an alias (admin/config/search/path/add), the prefix for Existing system path and Path alias is:
http://www.example.com/d8

This should be:
http://www.example.com/

I'm sure there are others. These were the problems I found in the first five minutes.

Workaround

1. Configure .htaccess as shown above
2. Implement https://www.drupal.org/node/2612160#comment-10637490
3. Implement https://www.drupal.org/node/2619432#comment-11012323

Items 2 and 3 don't appear to have received much in the way of community review, and may be a bit dodgy.

Fabianx’s picture

#31: What happens if you use:

RewriteBase /d8

in your .htaccess?

andypost’s picture

Also please keep in mind that it should work with nginx and likely in php-runserver

toomanypets’s picture

Regarding #31...

This page (https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteengine) states:

The RewriteBase directive specifies the URL prefix to be used for per-directory (htaccess) RewriteRule directives that substitute a relative path.

This directive is required when you use a relative path in a substitution in per-directory (htaccess) context unless either of the following conditions are true:

  1. The original request, and the substitution, are underneath the DocumentRoot (as opposed to reachable by other means, such as Alias).
  2. The filesystem path to the directory containing the RewriteRule, suffixed by the relative substitution is also valid as a URL path on the server (this is rare).

Item 1 is true, therefore RewriteBase is not required in my .htaccess file which is located in webroot.

Regardless of documentation I tested your suggestion. Modifed .htaccess in webroot:

RewriteBase /d8
RewriteRule ^$ $1 [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ $1 [L]

This also fails, as expected.

I also added "RewriteBase /d8" to the .htaccess file in the Drupal root.

This also fails.

znerol’s picture

I checked on Debian Jessie / Apache 2.4 / php 5.6.20 / mod_rewrite . Drupal 8.1 works in a subdirectory there out of the box, including css and alias paths (see attached screenshot).

So, people having troubles setting up Drupal 8 in a subdirectory, please open a support issue and post the relevant parts of your OS/webserver configuration there.

toomanypets’s picture

Regarding #35...

Your screenshot shows that you've accessed this site via http://localhost/drupal-8/about/ -- the installation subdirectory directory is not masked in the URL. I've never had a problem with this either.

The problem I reported in #29 refers to the scenario where we wish to hide (mask) the installation subdirectory in the URL using an .htaccess in webroot combined with $base_url.

Every site I've built with d6 and d7 has been located in subdirectory of webroot, using a top level .htaccess file and $base_url to mask the URL.

znerol’s picture

@toomanypets please just open a support request and describe your exact setup there.

Fabianx’s picture

#35: The use case is that Drupal 8 is in a sub-directory, but that the subdirectory should be invisible.

e.g.

www.host.com => /d8

But Drupal 8 just showing www.host.com for all generated urls.

znerol’s picture

Status: Active » Fixed

Re #35: The problem is that people used $base_url for many different things. This issue was reopened in #11 and #14 for different reasons, and #29 is yet another one. I'm pretty sure that we will need different solutions for them, therefore I'd suggest to go with separate support requests.

gaele’s picture

Status: Fixed » Active

This is not fixed.

People used $base_url for valid reasons, so this statement:

This option was only necessary on broken webserver configurations.

is incorrect. Please change this in the change record.

Fabianx’s picture

Status: Active » Fixed

I fixed the change record to remove the "annoying line", lets open up support requests for all 3 issues and link them as a child issue of this one, please.

I am pretty sure with some mod_rewrite magic it will be possible to do what you all want.

I am also working on pre-kernel middlewares, which should allow to change the request in a way, to override the requestUri before the Container is even loaded.

Edit:

It seems it is already possible to change the global request:

https://www.drupal.org/node/2619432#comment-11012323

This seems to be the easiest solution.

Fabianx’s picture

Status: Fixed » Needs work

Hm, I do think we really bugged this up.

The problem is:

- The goal of this issue was to ensure that we rely on Symfony for base url and base path, which would work fine.

However:

We still use server SCRIPT_NAME (from the request), which is not rewritten by .htaccess, but only the request url, hence people end up with the REQUEST_URI being correct and the $base_url being wrong ...

Therefore this really needs work.

Fabianx’s picture

To #42 here is a little script to reproduce:


use Symfony\Component\HttpFoundation\Request;

print_r('<pre>');
print_r($_SERVER);

require "../autoload.php";

$request = Request::createFromGlobals();

print_r([$request->getUri(), $request->getRequestUri(), $request->getBaseUrl(), $request->getBasePath()]);

    // Create base URL.
    $base_root = $request->getSchemeAndHttpHost();
    $base_url = $base_root;

    // For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is
    // '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'.
    if ($dir = rtrim(dirname($request->server->get('SCRIPT_NAME')), '\/')) {
      // Remove "core" directory if present, allowing install.php,
      // authorize.php, and others to auto-detect a base path.
      $core_position = strrpos($dir, '/core');
      if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) {
        $base_path = substr($dir, 0, $core_position);
       }
       else {
        $base_path = $dir;
       }
      $base_url .= $base_path;
      $base_path .= '/';
    }
    else {
      $base_path = '/';
    }
    $base_secure_url = str_replace('http://', 'https://', $base_url);
    $base_insecure_url = str_replace('https://', 'http://', $base_url);

print_r([$base_root, $base_url, $base_path, $base_secure_url, $base_insecure_url]);

Put that script as index.php in a directory called 'bar/' and add the following rewrite rule into the main .htaccess of Drupal 8:

  RewriteCond %{HTTP_HOST} ^d8-dev\.host\.com$ [NC]
  RewriteRule !^bar/   - [C]
  RewriteRule ^(.*)  bar/$1  [L]

Copy the original .htaccess to bar/ directory.

Now get http://d8-dev.host.com/node/1 and it returns:

Array
(
    [HTTP_HOST] => d8-dev.host.com
    [SERVER_NAME] => d8-dev.host.com
    [SERVER_PORT] => 80
    [DOCUMENT_ROOT] => /var/www/html
    [SCRIPT_FILENAME] => /var/www/html/bar/index.php
    [REDIRECT_URL] => /bar/node/1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => 
    [REQUEST_URI] => /node/1
    [SCRIPT_NAME] => /bar/index.php
    [PHP_SELF] => /bar/index.php
)
Array
(
    [0] => http://d8-dev.host.com/node/1
    [1] => /node/1
    [2] => 
    [3] => 
)
Array
(
    [0] => http://d8-dev.host.com
    [1] => http://d8-dev.host.com/bar
    [2] => /bar/
    [3] => https://d8-dev.host.com/bar
    [4] => http://d8-dev.host.com/bar
)

So Symfony's functions return a base_pathl() of '/' and Drupal's base_path() returns /bar.

That means the very mismatch this issue tried to prevent is still present, but now there is no way to 'fix' this anymore.

Changing that line to:

    if ($dir = rtrim(dirname($request->getBaseUrl()), '\/')) {

resolves the problem and did still work fine with the core/ scripts in my tests.

as prepareBaseUrl() has special code to deal with request_uri() being part of the base_url().

And as we call getUri() in page_cache anyway, this is not a performance concern either.

Fabianx’s picture

Status: Needs work » Fixed

Follow-up here: #2753591: Fix mismatch of $base_url with Symfony request object, lets leave this as fixed.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.