Operating a Drupal Site behind a Reverse Proxy Server (Apache)

Submitted by Stefan Schneider on Thu, 06/03/2010 - 16:04

It's often not desirable to expose your Drupal server directly to the end users. This document describes how to “hide” a Drupal server behind a reverse proxy server. This is typically done for a number of reasons:

  • protection: the topology of the server, the database server can be hidden from the front end

  • caching: The proxy server take away load from the backend system through caching

  • flexibility: the topology behind the reverse proxy can be changed more easily

  • scalability: the proxy server can be used for future load balancing

The technical problem isn't new. It has been solved before. I had however problems finding a solution in a single document.

The configuration is basically a platform neutral AMP stack consistent of 2 systems with

  • Drupal 6.16

The Drupal and Apache settings are independent of having used Solaris. They should be the same on any other operating system.

The reverse proxy is being build with Apache 2.2 listening to port 80. The task of the reverse proxy is to pass browser requests to the Drupal server and the response of the Drupal server back to the browser. We assume that the server name is proxyserver.example.com with the fictional IP address 10.0.0.1.

The Drupal server is named drupalserver.example.com with the fictional IP address 10.0.0.2.

Reverse Proxy Server Configuration

The proxy server gets configured through the httpd.conf file with the following directives:

/etc/opt/webstack/apache2/2.2/httpd.conf:

...

ServerName proxserver.example.com

...

ProxyRequests Off

ProxyPass / http://drupalserver.example.com/

ProxyPassReverse / http://drupalserver.example.com/

...

 

The three directives avoid that the Apache server acts as a regular proxy (ProxyRequests Off). The ProxyPass and ProxyPassReverse directives teach the server to relay all http requests ( / ) to drupalserver.example.com/ and the responses back. The ending slashes matter!

Start or restart the server with the following priviledged Solaris command:

bash-3.00# svcadm restart svc:/network/http:sun-apache22

The svcadm command starts the Apache server on Solaris as a service. This command assures that the service is up even after a reboot of the system. This is all it takes on the proxy side!

This setup of the reverse proxy does the job for pretty every simple web server behind it.

Drupal Server Setup

The Drupal server on the backend is a regular webstack 1.5 installation with Apache, PHP 5.2 and MySQL 5.0 . The Apache configuration of the back end server doesn't need anything specific for the the new proxy server front ending the configuration.

The difference to a normal Drupal configuration comes through the Drupal PHP settings. Drupal on the back end server needs to know two things to work in a reverse proxy context:

  1. True sender of the http request: It needs to analyze the X-Forwarded-For field in the http headers to know where the originator of the http request comes from since the http request itself now always comes from the reverse proxy server. It's supposed however to analyze the X-Forwarded-For parameters for requests coming from the reverse proxy exclusively.

  2. True domain name: Drupal encodes the domain name in the cookie. The Drupal cookie management needs to be changed in order to match the fact that the browsers think that the Drupal server is located in proxyserver.example.com

Edit the Drupal settings file on drupalserver.example.com file

../htdocs/sites/default/settings.php:

/**

* Drupal automatically generates a unique session cookie name for each site

* based on on its full domain name. If you have multiple domains pointing at

* the same Drupal site, you can either redirect them all to a single domain

* (see comment in .htaccess), or uncomment the line below and specify their

* shared base domain. Doing so assures that users remain logged in as they

* cross between your various domains.

*/

$cookie_domain = 'proxyserver.example.com';

 

$conf = array(

* Enable this setting to determine the correct IP address of the remote

* client by examining information stored in the X-Forwarded-For headers.

* X-Forwarded-For headers are a standard mechanism for identifying client

* systems connecting through a reverse proxy server, such as Squid or

* Pound. Reverse proxy servers are often used to enhance the performance

* of heavily visited sites and may also provide other site caching,

* security or encryption benefits. If this Drupal installation operates

* behind a reverse proxy, this setting should be enabled so that correct

* IP address information is captured in Drupal's session management,

* logging, statistics and access management systems; if you are unsure

* about this setting, do not have a reverse proxy, or Drupal operates in

* a shared hosting environment, this setting should be set to disabled.

*/

'reverse_proxy' => TRUE,

/**

* reverse_proxy accepts an array of IP addresses.

*

* Each element of this array is the IP address of any of your reverse

* proxies. Filling this array Drupal will trust the information stored

* in the X-Forwarded-For headers only if Remote IP address is one of

* these, that is the request reaches the web server from one of your

* reverse proxies. Otherwise, the client could directly connect to

* your web server spoofing the X-Forwarded-For headers.

*/

'reverse_proxy_addresses' => array('10.0.0.1',),

);

 

Trouble Shooting

No Authentification in Drupal possible

Symptom: The site works behind the proxy. Log in with user credentials doesn't work.

Solution: At least the cookie domain “ $cookie_domain = 'proxyserver.example.com';” in the file settings.php isn't set.

Hint: The open source load generator Faban helped me a lot. It allows to send http requests to the proxy server. Faban actually knows about cookies and it complains:

$ fhb -r 5/120/5 -c 2 -W 100 http://proxyserver.example.com/?q=ha

...

WARNING: Cookie rejected: "SESS1e661ae9a28c0626f28bb6cef67cf393=4pllfu49c6sd8l00007emfieb1". Illegal domain attribute ".drupalserver.example.com ". Domain of origin: "proxyserver.example.com"

The command above makes Faban to have 2 threads (-c) which fire every 100ms (-W) the URL with 5 seconds ramp on, off ramp and a steady state period of 120s.

Style Sheets and other Parts of the Page missing

Symptom: The page is getting rendered in a partial way. Some files are missing.

Solution: The httpd.conf file of the reverse proxy has most likely some slashes missing in the directives. The URLs are not getting resolved, or assembled the right way. Make sure that the red slashes below are part of the configuration.

ProxyPass / http://drupalserver.example.com/

ProxyPassReverse / http://drupalserver.example.com/

References