8

In settings.php there is the section "Reverse Proxy Configuration". It suggests settings to add when you are using a reverse proxy and SSL termination.

Nginx is listening on port 443, adds the headers and then passes to Apache Httpd on localhost port 80. Here is the Nginx as described:

    server {
        listen 443 ssl http2;

        server_name example.com;
        ssl_certificate /etc/nginx/ssl/nginx.crt;
        ssl_certificate_key /etc/nginx/ssl/nginx.key;

        location / {
            proxy_pass http://127.0.0.1:80;
            proxy_set_header X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header X-Forwarded-Port 443;
            proxy_set_header Host $host;
        }
}

With these settings/headers in mind what selection would be correct from the 'Reverse Proxy Configuration;' in settings.php?

 * Reverse Proxy Configuration:
 *
 * 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. In an environment where Drupal
 * is behind a reverse proxy, the real IP address of the client should
 * be determined such that the correct client IP address is available
 * to Drupal's logging, statistics, and access management systems. In
 * the most simple scenario, the proxy server will add an
 * X-Forwarded-For header to the request that contains the client IP
 * address. However, HTTP headers are vulnerable to spoofing, where a
 * malicious client could bypass restrictions by setting the
 * X-Forwarded-For header directly. Therefore, Drupal's proxy
 * configuration requires the IP addresses of all remote proxies to be
 * specified in $settings['reverse_proxy_addresses'] to work correctly.
 *
 * Enable this setting to get Drupal to determine the client IP from
 * the X-Forwarded-For header (or $settings['reverse_proxy_header'] if set).
 * If you are unsure about this setting, do not have a reverse proxy,
 * or Drupal operates in a shared hosting environment, this setting
 * should remain commented out.
 *
 * In order for this setting to be used you must specify every possible
 * reverse proxy IP address in $settings['reverse_proxy_addresses'].
 * If a complete list of reverse proxies is not available in your
 * environment (for example, if you use a CDN) you may set the
 * $_SERVER['REMOTE_ADDR'] variable directly in settings.php.
 * Be aware, however, that it is likely that this would allow IP
 * address spoofing unless more advanced precautions are taken.
 */
# $settings['reverse_proxy'] = TRUE;

/**
 * Specify every reverse proxy IP address in your environment.
 * This setting is required if $settings['reverse_proxy'] is TRUE.
 */
# $settings['reverse_proxy_addresses'] = array('a.b.c.d', ...);

/**
 * Set this value if your proxy server sends the client IP in a header
 * other than X-Forwarded-For.
 */
# $settings['reverse_proxy_header'] = 'X_CLUSTER_CLIENT_IP';

/**
 * Set this value if your proxy server sends the client protocol in a header
 * other than X-Forwarded-Proto.
 */
# $settings['reverse_proxy_proto_header'] = 'X_FORWARDED_PROTO';

/**
 * Set this value if your proxy server sends the client protocol in a header
 * other than X-Forwarded-Host.
 */
# $settings['reverse_proxy_host_header'] = 'X_FORWARDED_HOST';

/**
 * Set this value if your proxy server sends the client protocol in a header
 * other than X-Forwarded-Port.
 */
# $settings['reverse_proxy_port_header'] = 'X_FORWARDED_PORT';

/**
 * Set this value if your proxy server sends the client protocol in a header
 * other than Forwarded.
 */
# $settings['reverse_proxy_forwarded_header'] = 'FORWARDED';

Testing host settings that end up being passed to PHP (7) by adding the following to the bottom of the setting.php:

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

The headers added by Nginx are being passed down but the Server URI is always set to [SCRIPT_URI] => http://www.example.com/ rather than [SCRIPT_URI] => https://www.example.com/.

What would be the correct reverse proxy Drupal settings in settings.php to get things working properly?

5 Answers 5

6

This should work for both Drupal 7 and 8. It's actually the global.inc for one of my Aegir set-ups, which gets injected into all hosted sites' settings.php files.

<?php # global settings.php

/**
 * Tell all Drupal sites that we're running behind an HTTPS proxy.
 */

// Drupal 7 configuration.
if (explode('.', VERSION)[0] == 7) {
  $conf['reverse_proxy'] = TRUE;
  $conf['reverse_proxy_addresses'] = ['1.2.3.4', ...];

  // Force the protocol provided by the proxy. This isn't always done
  // automatically in Drupal 7. Otherwise, you'll get mixed content warnings
  // and/or some assets will be blocked by the browser.
  if (php_sapi_name() != 'cli') {
    if (isset($_SERVER['SITE_SUBDIR']) && isset($_SERVER['RAW_HOST'])) {
      // Handle subdirectory mode.
      $base_url = $_SERVER['HTTP_X_FORWARDED_PROTO'] . '://' . $_SERVER['RAW_HOST'] . '/' . $_SERVER['SITE_SUBDIR'];
    }
    else {
      $base_url = $_SERVER['HTTP_X_FORWARDED_PROTO'] . '://' . $_SERVER['SERVER_NAME'];
    }
  }
}
// Drupal 8 configuration.
else {
  $settings['reverse_proxy'] = TRUE;
  $settings['reverse_proxy_addresses'] = ['1.2.3.4', ...];
}
1
1

This is part of settings.php that I'm using on Google Cloud:

// Force secure connection if the request wasn't made from CLI.
if (PHP_SAPI !== 'cli') {
  // Fix HTTPS if we're behind load balancer.
  if (getenv('HTTPS') !== 'on' && getenv('HTTP_X_FORWARDED_PROTO') === 'https') {
    $_SERVER['HTTPS'] = 'on';
  }

  // Perform redirect to secure connection if needed.
  if ($_SERVER['HTTPS'] !== 'on') {
    header('HTTP/1.0 301 Moved Permanently');
    header('Location: https://'. getenv('HTTP_HOST') . getenv('REQUEST_URI'));
    exit;
  }
}

//...

// Enable LB headers, if applicable.
if (getenv('HTTP_VIA')) {
  $settings['reverse_proxy'] = TRUE;
  $settings['reverse_proxy_addresses'] = ['0.0.0.0']; // LB IP
}

Have a look at https://www.drupal.org/node/2492389 and also see which headers you are receiving so that you can adjust Drupal's values accordingly.

0

I used @colan 's excellent solution as a base, and modified 2 lines so that there is no need to manually define the reverse_proxy_addresses.

I used this solution with great success for a site sitting behind an SSL-Terminating nginx reverse-proxy in kubernetes (nginx-ingress)

/**
 * Tell all Drupal sites that we're running behind an HTTPS proxy.
 */

// Drupal 7 configuration.
if (explode('.', VERSION)[0] == 7) {
  $conf['reverse_proxy'] = TRUE;
  $conf['reverse_proxy_addresses'] = array($_SERVER['REMOTE_ADDR']);

  // Force the protocol provided by the proxy. This isn't always done
  // automatically in Drupal 7. Otherwise, you'll get mixed content warnings
  // and/or some assets will be blocked by the browser.
  if (php_sapi_name() != 'cli') {
    $base_url = $_SERVER['HTTP_X_FORWARDED_PROTO'] . '://' . $_SERVER['SERVER_NAME'];
  }
}
// Drupal 8 configuration.
else {
  $settings['reverse_proxy'] = TRUE;
  $settings['reverse_proxy_addresses'] = array($_SERVER['REMOTE_ADDR']);
}

Dislaimer: (Thanks to @DavidStinemetze)

While this solution solves a unique problem of being able to serve a site from behind a CDN or service with unpredictable IPs, it is likely that this method would allow IP address spoofing unless more advanced precautions are taken. So make sure to take these precautions to prevent spoofing

From default settings.php:

 * In order for this setting to be used you must specify every possible
 * reverse proxy IP address in $settings['reverse_proxy_addresses'].
 * If a complete list of reverse proxies is not available in your
 * environment (for example, if you use a CDN) you may set the
 * $_SERVER['REMOTE_ADDR'] variable directly in settings.php.
 * Be aware, however, that it is likely that this would allow IP
 * address spoofing unless more advanced precautions are taken.
6
  • 1
    Per the default.settings.php code comments: " * In order for this setting to be used you must specify every possible * reverse proxy IP address in $settings['reverse_proxy_addresses']. * If a complete list of reverse proxies is not available in your * environment (for example, if you use a CDN) you may set the * $_SERVER['REMOTE_ADDR'] variable directly in settings.php. * Be aware, however, that it is likely that this would allow IP * address spoofing unless more advanced precautions are taken." See: api.drupal.org/api/drupal/… Aug 13, 2018 at 16:57
  • Hi, so the point of this comment is so that I will understand to "Be aware, however, that it is likely that this would allow IP * address spoofing unless more advanced precautions are taken" ? Or is there some better way of taking care of this situation where the remote address is unknown?
    – yosefrow
    Aug 14, 2018 at 7:37
  • @DavidStinemetze Hey, I added a disclaimer to the answer. Thanks for the heads up.
    – yosefrow
    Aug 14, 2018 at 7:48
  • This is precisely why I didn't do it this way. Don't trust anything from the client! ;)
    – colan
    Nov 6, 2018 at 22:59
  • 1
    Shouln't that be $settings['reverse_proxy_addresses'] not $conf['reverse_proxy_addresses']?
    – Jonathan
    Jan 10, 2019 at 14:39
0

Drupal 8

Apply the following settings in site's settings.php file:

$settings['reverse_proxy'] = TRUE;
$settings['reverse_proxy_addresses'] = [@$_SERVER['REMOTE_ADDR']]

It should work for CDN where IP addresses are dynamic.

This is explained in the following comment:

 * In order for this setting to be used you must specify every possible
 * reverse proxy IP address in $settings['reverse_proxy_addresses'].
 * If a complete list of reverse proxies is not available in your
 * environment (for example, if you use a CDN) you may set the
 * $_SERVER['REMOTE_ADDR'] variable directly in settings.php.
 * Be aware, however, that it is likely that this would allow IP
 * address spoofing unless more advanced precautions are taken

Drupal 7

$conf['reverse_proxy'] = TRUE;
$conf['reverse_proxy_addresses'] = [@$_SERVER['REMOTE_ADDR']];

See also:

0

If you have a dynamic reverse proxy IP, but you know it should be on the internal network (e.g kubernetes cluster) to validate the remote_addr is internal first you can use something like this in settings.php (D8):

/**
 * Helper function to check
 * if IP address is internal network
 * @param string $ip - IP address to test
 * @return boolean - false if private/internal IP
 */
function is_public_ip($ip) {
  return (bool)filter_var(
    $ip,
    FILTER_VALIDATE_IP,
    FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
  );
}

// Set reverse proxy config for correct client IP lookup (uses env var check)
if (getenv('DRUPAL_REVERSE_PROXY')) {
  $settings['reverse_proxy'] = TRUE;
  // Init address list, no overwrite
  $settings['reverse_proxy_addresses'] = $settings['reverse_proxy_addresses'] ?? [];
  // Get dynamic forwarded for or remote IP for proxy config
  // Check x-forwarded-for
  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $forwarded_for_ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
    foreach ($forwarded_for_ips as $forwarded_for_ip) {
      // Not already added and is not public ip (is internal ip)
      if (!in_array($forwarded_for_ip, $settings['reverse_proxy_addresses']) && !is_public_ip($forwarded_for_ip)) {
        $settings['reverse_proxy_addresses'][] = $forwarded_for_ip;
      }
    }
  }
  // Check remote_addr to add as well just in case not added above
  if (!empty($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $settings['reverse_proxy_addresses'])) {
    // Ensure is internal IP
    if (!is_public_ip($_SERVER['REMOTE_ADDR'])) {
      $settings['reverse_proxy_addresses'][] = $_SERVER['REMOTE_ADDR'];
    }
  }
  // No reverse_proxy for this case, as proxy address is required, use default
  if (empty($settings['reverse_proxy_addresses'])) {
    $settings['reverse_proxy'] = FALSE;
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.