Hi everybody,

I want to share my experience in setting up Drupal in a way that it can be run through a SSL Proxy using a shared certificate as it can be found with Hosteurope. I assume that other SSL Proxys work similar as the one of Hosteurope, but I lack the resources (in webspace) to test.

If you want to use https with a provider like Hosteurope, you can't use your domain name directly. Instead you must call your site by appending your domain name to a special SSL Proxy URL given by the provider. The SSL Proxy will then forward the request internally via regualr http (without the s). So your Drupal website will be called from the proxy and will return the page back to the proxy which returns the page back to the original caller.
The main problem is that your Drupal site

  • will not know that it is really a https request.
  • It also will not know that it has to replace the $base_url with the URL of the SSL Proxy.

When searching the web for this topic, you will most probably find the articles here

I took this article as starting point, but instead of hacking / patching the Drupal core, my solution will reside entirely in the the settings.php file:

01:  $request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'ssl.proxy.org') ? 'SSL' : 'NONSSL';
02:  if($request_type!="SSL"){
03:    //header("Location:https://ssl.proxy.org/www.example.com");
04:    $base_url = 'http://www.example.com';  // NO trailing slash!
04a:   $base_url = 'http://www.example.com/some/dir';  // NO trailing slash!
05:    $cookie_domain = 'www.example.com';
06:  } else {
07:    $base_url = 'https://ssl.proxy.org/www.example.com';  // NO trailing slash!
07a:   $base_url = 'https://ssl.proxy.org/www.example.com/some/dir';  // NO trailing slash!
08:    $cookie_domain = 'ssl.proxy.org';
09:    $_SERVER['HTTPS']='on';
10:    $_SERVER['REQUEST_URI']='/www.example.com'. $_SERVER['REQUEST_URI'];
10a:   $_SERVER['REQUEST_URI']='/www.example.com'. $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!
11:    $conf = array(
12:            'reverse_proxy' => TRUE,
13:            'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),	
14:    );
15:  }

Obviously, replace www.example.com with your domain and ssl.proxy.org with the domain name of your SSL Proxy.

01:  $request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'ssl.proxy.org') ? 'SSL' : 'NONSSL';

Hosteuropes SSL Proxy sends its domain name in $_SERVER['HTTP_X_FORWARDED_HOST']. If you are running a different SSL Proxy, you may want to check the content $_SERVER if this doesn't work. (I didn't check any standards on this)

02:  if($request_type!="SSL"){
03:    //header("Location:https://ssl.proxy.org/www.example.com");
04:    $base_url = 'http://www.example.com';  // NO trailing slash!
04a:   $base_url = 'http://www.example.com/some/dir';  // NO trailing slash!
05:    $cookie_domain = 'www.example.com';

Actually lines 04 and 05 may be unnecessary, since Drupal will figure this out by itself, but for clarity I left the code in. Setting $base_url bypasses deriving it from the URL.

Uncomment line 03 if you want permanent redirection to your site.

Note: 04a shows what to do, if your site is located within /some/dir.

07:    $base_url = 'https://ssl.proxy.org/www.example.com';  // NO trailing slash!
07a:   $base_url = 'https://ssl.proxy.org/www.example.com/some/dir';  // NO trailing slash!

Since the request that comes from the SSL Proxy comes in from http://www.example.com we must make Drupal think it came from this URL. This line will result in returning resources like CSS, images and Java scripts with a correct URL, since the the SSL Proxy (at Hosteurope) replaces the sites domain name with its own domain name, ending up with links to http://ssl.proxy.org/module/....
Note: 07a shows what to do, if your site is located within /some/dir.

08:    $cookie_domain = 'ssl.proxy.org';

I set my browser to accept cookies only from the same domain as the URL, so we need to change the $cookie_domain to the SSL proxys domain name. (i.e.: with out this line, I was not able to login)

09:    $_SERVER['HTTPS']='on';

Modules as Secure Pages need to know, if the request is done via https. Since the request is done by the SSL Proxy via http, we must fake a https request.

10:    $_SERVER['REQUEST_URI']='/www.example.com'. $_SERVER['REQUEST_URI'];
10a:   $_SERVER['REQUEST_URI']='/www.example.com'. $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!

Without this line, everything worked except for forms. This is why http://drupal.org/node/60222 suggests to hack common.inc. I wondered, why the URL given to theme_form() was wrong in the first place, but it took me quite a while to figure out, where the URL was created:
The URL is set in request_uri().
At least Hosteurops SSL Proxy sets $_SERVER['REQUEST_URI'], so all I had to do was to prepend the my domain name to making Drupal insert the correct URL.
Note: Thanks to fumir (Post Problem with additional directories) for pointing this out:
$_SERVER['REQUEST_URI'] already contains the '/some/dir' part. So you must not replace www.example.com with www.example.com/some/dir here.

11:    $conf = array(
12:            'reverse_proxy' => TRUE,
13:            'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),	
14:    );

In order to make Drupal log the IP-adress of the orignal caller (instead of the SSL Proxys IP), we must enable reverse_proxy and set the IP adress of the SSL Proxy.
For Hosteurope, the IP changed between different calls (probably due to loadbalancing), so I decided to set the reverse_proxy_addresses from the appropriate $_SERVER entry.

That was it: No fumbling the Drupal core, no hacking any .tpl.php files and other stuff, you really don't want to do.

Comments

Apfel007’s picture

Hi Skybow,

Today I tried several hours your solution.. It looks obvious, but it work not for me - in that moment..
Are you sure that you have no things in your .htaccess for this ssl-action ?
I could secure-pages satisfy to know that ssl is possible - ( I forced $_SERVER = 'ssl' ).
I got a rediction to http://www.ssl-id.net/....../install.php - is there a redict necessary? I thing some pathes go wrong in drupal?
Do you have some more experience with it?

Cheers

fumir’s picture

Hi skybow,

thanks for this solution.
It works fine, with just one little problem:

My http url is like "www.example.com/some/dir" and therefore I replaced all your "www.example.com" by "www.example.com/some/dir" .
After using your code all seems ok, except the forms (page not found).
At the delivered html code the part "/some/dir" was doubled into the form action url.

I fix it by changing line 10 from
10: $_SERVER['REQUEST_URI']='/www.example.com/some/dir'. $_SERVER['REQUEST_URI'];
to
10: $_SERVER['REQUEST_URI']='/www.example.com'. $_SERVER['REQUEST_URI'];
( but don't understand how it works :-)

cheers

skybow’s picture

Hi fumir,

thanks for pointing this out. I updated my instructions, so take a look there.

cheers

Bodo Maass’s picture

Hi skybow,
Thanks for these instructions. This seems to work for me on a Drupal 5.15 test installation with an SSL Proxy.

Sorry, I wrote too soon. It did seem to work on a clean empty site, but after enabling my production modules I get some problems.

abcprabhu’s picture

Hi!

I'm using D6 - http://www.gradprep.org/application and SSL url https://secure560.sectorlink.com/gradprep/application

Can any one tell me what should I have to do with request_uri()?

I have facing the following issues:
clean url is not working in the SSL url
theme is not working
form action is not working
base url is set as https://secure560.sectorlink.com/application (It has to be like this: https://secure560.sectorlink.com/gradprep/application)

Please any one help me on this.

Regards,
IT Web - Q Solutions
Providing web solutions for open source communities

skybow’s picture

Hi abcprabhu,

I used Safari to compare the paths of the resources of both the "http" and "https" links.
If you take a look at it, you will find, that for node.css it will look like this:

http://www.gradprep.org/application/modules/node/node.css?l
https://secure560.sectorlink.com/application/modules/node/node.css?l

This is because you set the $base_url in line 07a to what you already posted:

07a:   $base_url = 'https://secure560.sectorlink.com/application';  // NO trailing slash!

You may try to set $base_url to

07a:   $base_url = 'https://secure560.sectorlink.com/gradprep/application';  // NO trailing slash!

in order to have the correct resource URLs generated.

HTH
skybow

abcprabhu’s picture

Hi!

This is the code, what I have used in the settings.php file

 $request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'secure560.sectorlink.com') ? 'SSL' : 'NONSSL';
 if($request_type!="SSL"){
  $base_url = 'http://www.gradprep.org/application';  // NO trailing slash!
  $cookie_domain = 'http://www.gradprep.org/application';
 } else {
 $base_url = 'https://secure560.sectorlink.com/gradprep/application';  // NO trailing slash!
 $cookie_domain = 'https://secure560.sectorlink.com/gradprep/application';
 $_SERVER['HTTPS']='on';
 $_SERVER['REQUEST_URI']='gradprep.org'. $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!
 $conf = array(
 'reverse_proxy' => TRUE,
 'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),
 );
 }

Regards,
Prabhu.c

skybow’s picture

Hi,

  1. I'm not sure, if this is a problem, but at least your $cookie_domain entries may be a problem. I think they should only contain the domain name. Especially for the SSL based access this has to be the domain name entry of the SSL proxy.
  2. I compared your settings with what I wrote in my initial post: In Line 10a there is a leading slash, which you don't have in your config.
$request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'secure560.sectorlink.com') ? 'SSL' : 'NONSSL';
if($request_type!="SSL"){
  $base_url = 'http://www.gradprep.org/application';  // NO trailing slash!
  $cookie_domain = 'www.gradprep.org';
} else {
  $base_url = 'https://secure560.sectorlink.com/gradprep/application';  // NO trailing slash!
  $cookie_domain = 'secure560.sectorlink.com';
  $_SERVER['HTTPS']='on';
  $_SERVER['REQUEST_URI']='/www.gradprep.org'. $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!
  $conf = array(
    'reverse_proxy' => TRUE,
    'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),
  );
}

abcprabhu’s picture

Hi!

I have incorporated your code, but problem still exist.

Actually the base url is causing this problem, it is pointing at secure560.sectorlink.com/application which should be secure560.sectorlink.com/gradprep/application

The Non-SSL Base path is pointing at http://www.gradprep.org/application which may also pointing SSL Base path?

This is current code I'm using in the settings.php

 $request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'secure560.sectorlink.com') ? 'SSL' : 'NONSSL';
 if($request_type!="SSL"){
  $base_url = 'http://www.gradprep.org/application';  // NO trailing slash!
  $cookie_domain = 'www.gradprep.org';
 } else {
 $base_url = 'https://secure560.sectorlink.com/gradprep/application';  // NO trailing slash!
 $cookie_domain = 'secure560.sectorlink.com';
 $_SERVER['HTTPS']='on';
 $_SERVER['REQUEST_URI']='/www.gradprep.org'. $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!
 $conf = array(
 'reverse_proxy' => TRUE,
 'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),
 );
 }

Thanks for your help.

Regards,
Prabhu.c

skybow’s picture

Hi,

Something that may be the solution for this (and the source for others with similar problems) just popped into my mind, so please try this one and tell me if it works:

$_SERVER['REQUEST_URI']='/gradprep'. $_SERVER['REQUEST_URI'];

Explanation:
In my setup the SSL proxy uses the full domain name of the original site in the SSL based URL.
Your SSL proxy uses only part of the domain name: gradprep.
The reason for modifying $_SERVER['REQUEST_URI'] was to make sure, that links to other resources use a correct SSL based URL (with the domain name of the host (SSL or non SSL) omitted in the $_SERVER['REQUEST_URI'] ).
So you need to append what the SSL proxy uses in the URL to identify your site.

abcprabhu’s picture

Hi!

Still there is no changes! For your information, we are using a shared SSL certification.

Thanks for your help.

Regards,
Prabhu.c

kccmcck’s picture

Thanks for this tip, skybow. I am having difficulty implementing this solution for two reasons:

  1. I am receiving the following error on my homepage "Notice: Undefined index: HTTP_X_FORWARDED_HOST in /var/www/singlesite/thenotoriousfig.com/sites/default/settings.php on line 129"
  2. My Ubercart cart contents do not carry over when switching from the http site to the https site. The secure cart is always empty.

Take a look here.

skybow’s picture

  • I just double checked the content of the $_SERVER variable in my setup by creating a "page" node with Filter settings "PHP code" and the following code:
    print_r($_SERVER);

    For "http://..." there is no HTTP_X_FORWARDED_HOST in $_SERVER but I don't get an 'Undefined index' message.
    Please note that the message is declared as "Notice", maybe there is an option to not show notice level messages?
    The other reason I can think of is PHP and/or HTTP Server version: My host provides PHP 5.2.10 on an Apache 2.2.

  • Regarding the Ubercart:
    When switching from HTTP to HTTPS you also switch the cockie context which means that you can't take over any state info from HTTP over to HTTPS. When I tried the whole SSL stuff, I faced a similar problem: After login, switching back to HTTP leads to not being logged in again. A second login will switch to HTTPS where the cockie shows that I'm logged in already.
    The cockie takes the Drupal session ID so unless there is a way to put the Drupal session ID into the URL, there is probably no way to move any state info across the HTTP => HTTPS boundary.
wicki’s picture

Hello,

I have my site running with oscommerce engine, though this forum is not related with that engine but my problem is similar as I am using a shared ssl certificate. so I hope someone can give me good clues.

configure.php file snapshot:

define('HTTP_SERVER', 'http://mystore.com'); // eg, http://localhost - should not be empty for productive servers
define('HTTPS_SERVER', 'https://membername.sslpowered.com/mystore.com'); // eg, https://localhost - should not be empty for productive servers
define('ENABLE_SSL', true); // secure webserver for checkout procedure?
define('HTTP_COOKIE_DOMAIN', 'mystore.com');
define('HTTPS_COOKIE_DOMAIN', 'membername.sslpowered.com');

I have installed a module to process credit card master card payments on my website. so here are the steps when a customer buys and pays through credit card.

1: When a customer confirms the checkout, my store is directed to:

cap.securecode.com

customer enters the securecode it does some processing but then it cannot find one of the file on my server. That is because it(cap.securecode.com) has a wrong url, it is adding an additional mystore.com to the original url i.e

membername.sslpowered.com/mystore.com/mystore.com/checkout_process.php...

the additional mystore.com should not be there the correct URL would be

membername.sslpowered.com/mystore.com/checkout_process.php...

there is a file which is sending request and receiving response from the credit card\mastercard validation server and in that file I have a return URL of which script is:

$get_string = "";
if (tep_not_null($HTTP_POST_VARS['PaRes']))
{
$get_string = "&TRX=" . $_SESSION['ReciptNumber'] . "&GUID=" . $_SESSION['GUID'];
}
//
// Create the return page URL to come back from VBV (or similar)
if (ENABLE_SSL == true) $return_url = HTTPS_SERVER;
else $return_url = HTTP_SERVER;

if ( ($_SERVER['SERVER_PORT'] != "80") && ($_SERVER['SERVER_PORT'] != "443") && tep_not_null($_SERVER['SERVER_PORT']) )
{
$return_url .= ':' . $_SERVER['SERVER_PORT'];

}

$return_url .= $_SERVER['REQUEST_URI'] . $get_string; // REQUEST_URI includes osCid!!

now when I echo this return url, I get: membername.sslpowerd.com/mystore.com/mystore.com/checkout_process.php...

while it should be membername.sslpowerd.com/mystore.com/checkout_process.php...

and so I get a 404 error.

is there anybody to give me clues.

Thanks

baerchenland’s picture

thanks to skybow, I was able to have my site run on the SSL-Proxy of Host-Europe.
I'D like to ensure that every request is redirected to the SSL-Proxy, but I run into problems.

The only rewrite I got to work was:
RewriteCond %{HTTP_X_FORWARDED_HOST} !^ssl\.webpack\.de [NC]
RewriteRule ^example.com/(.*) https://ssl.webpack.de/example.com/$1 [R]

But strangely, this works only on "second thought", meaning I get the contents of my site dieplayed correctely but without the URl showing ssl.webpack.de and without having the SSL-lock in the status bar. Once, I use any link on the site, everything is cor-*rect (I get the SSL-URL and the lock)

Anyone got an idea why this is happening?

sylentmode’s picture

We use a non-www reverse proxy eg http://reverseproxy.domain.com/root/drupal
the cookie_domain variable gets mangled to .domain.com, and will therefore never match. This is done in the bootstrap file around line 1230

$cookie_domain = '.'. $cookie_domain[0];

this line should be commented out, or the logic should be rewritten as not everyone has www

Unless I missed something.

sepp68’s picture

at all-inkl.com in Germany

with following Code

$request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'ssl-account.com') ? 'SSL' : 'NONSSL';
if($request_type!="SSL"){
	; // NOTHING  because it is a non https -request
  } 
else {
    $base_url = 'https://ssl-account.com/example.com';  // NO trailing slash!
    $cookie_domain = 'ssl-account.com';
    $_SERVER['HTTPS']='on';
    $_SERVER['REQUEST_URI']='example.com'. $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!
    $conf = array(
            'reverse_proxy' => TRUE,
            'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),
    );
}

Please watch $_SERVER['REQUEST_URI'] is the second part of the base-url

mabo1972’s picture

at all-inkl.com Germany

i changed the line

$request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'ssl-account.com') ? 'SSL' : 'NONSSL';

to

$request_type = (getenv('HTTPS') == '1' || getenv('HTTPS') == 'on' || !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) ? 'SSL' : 'NONSSL';

and now it works with module "securepages" enabled in Drupal 7

many thx for this article

mabo

Anonymous’s picture

I am currently setting this up for a website hosted by strato. Most of it works, but the images included in some nodes show up with the wrong URL. They use a relative path, which needs to be extended by an additional path...

Here is what I have in settings.php:

$request_type = ($_SERVER['HTTP_X_FORWARDED_HOST'] == 'www.ssl-id.de') ? 'SSL' : 'NONSSL';
if($request_type!="SSL"){
    $base_url = 'http://www.mydomain.de';  // NO trailing slash!
    $cookie_domain = 'mydomain.de';
  } else {
    $base_url = 'https://www.ssl-id.de/mydomain.de';  // NO trailing slash!
    $cookie_domain = 'ssl-id.de';
    $_SERVER['HTTPS']='on';
    $conf = array(
            'reverse_proxy' => TRUE,
            'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']),
    );
  }

REQUEST_URI is already set correctly by the ssl-proxy, as it seems.

My problem is now that images I have included in Articles show up as www.ssl-id.de/site/... instead of www.ssl-id.de/mydomain.de/site/...

I use the image module, image cache, boost and some others.

[EDIT] partially solved: the problem was caused by using a wysiwig editor (CKEditor). This editor put in the relative paths. Installing the pathologic module and setting / to also considered as local solved the problem (at least as far as I can tell now). However now the site is showing as partially insecure, as the images and some other parts are not served via https.

Admin Menu is still not working correctly (works either with SSL or without, depending where I flushed the cache. It does not work in both. As logged in tasks should go via ssl anyway that probably is not a big issue.

thanks,Nils

sven_xo’s picture

still got the same Problem with Images & Editorstuff.
Any better solutions?

BrianLewisDesign’s picture

moved

BrianLewisDesign’s picture

Here is the D7 setup that works for me on A2 shared hosting, with a shared certificate.

  1. /sites/default/settings.php - for the $base_url and $cookie_domain
  2. /.htaccess - https and remove www, because shared url can't have www
  3. pathologic module - do // protocol for the image paths
  4. securepages module - via shared https url, so you can turn it on
// (/sites/default/settings.php)
$request_is_ssl = (getenv('HTTPS') == '1' || getenv('HTTPS') == 'on' || !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) ? TRUE : FALSE;
if ($request_is_ssl) {
  $base_url = 'https://mysite-wwwls7.ssl.supercp.com';
  $cookie_domain = '.mysite-wwwls7.ssl.supercp.com';  
  $_SERVER['HTTPS'] = 'on';  
  $conf = array('reverse_proxy' => TRUE, 'reverse_proxy_addresses' => array($_SERVER['REMOTE_ADDR']));
  //$_SERVER['REQUEST_URI'] = 'mysite-wwwls7.ssl.supercp.com' . $_SERVER['REQUEST_URI']; // Only the DNS name of the site needed here!
} else {  
  $base_url = 'http://mysite.com';  // NO trailing slash!
  $cookie_domain = '.mysite.com';
  $_SERVER['HTTPS'] = '';
}
# (/.htaccess)
RewriteRule ^ - [E=protossl]
RewriteCond %{HTTPS} on
RewriteRule ^ - [E=protossl:s]
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
/* pathologic module: (/admin/config/content/formats) > each text format > Correct URLs with Pathologic > Protocol relative URL http://mysite.com/ https://mysite-wwwls7.ssl.supercp.com/ (Save configuration) */
/* securepages module: (https://mysite-wwwls7.ssl.supercp.com/admin/config/system/securepages) > Enabled, Switch back to http pages when there are no matches, http://mysite.com, https://mysite-wwwls7.ssl.supercp.com, Make secure only the listed pages (Save configuration) */
a.milkovsky’s picture

Thank you for sharing. Could anybody share the Drupal 8 implementation?