Background: I host a WordPress site with a hosting company that places a combined Varnish server + SSL terminator system upstream of my web server. The WordPress site runs on Apache and is accessible over both HTTP and HTTPS via the Varnish+SSL terminator.

The setup looks like this: Imgur

Image credit: DigitalOcean (For reference, DigitalOcean is not my host but their image precisely describes the setup at my host.)

I do not have administrative access to the Varnish+SSL system.

All the local traffic between the SSL terminator, Varnish, and my web server are unencrypted.

Objective: I wish to have the WordPress admin interface accessible only over a secure (HTTPS) connection. Users should be able to access the WordPress site over HTTP or HTTPS at their choosing, with the default being HTTP.

Relevant Configuration: Since the web server itself does not know if the initial connection was secure, WordPress goes into a redirect loop if I force the admin interface to be accessible only over HTTPS using define('FORCE_SSL_ADMIN', true); in my wp-config.php file.

However, the Varnish+SSL terminator system does include headers to indicate what protocol was used, so I am using the following working configuration:

define('FORCE_SSL_ADMIN', true);
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
       $_SERVER['HTTPS']='on';

Thus, WordPress will automatically recognize if a visitor is accessing the site over HTTP or HTTPS and will modify links to CSS/JavaScript files appropriately to avoid "insecure content" warnings.

Problem: Although WordPress will modify links based on protocol to prevent "insecure content" warnings, Varnish doesn't care about protocols and will cache content (regardless of protocol) until it expires.

As an example, if Varnish starts with an empty cache and the first visitor is using HTTPS, WordPress modifies all the links to refer to HTTPS and returns the requested content. Varnish caches this content with the HTTPS links. If the second visitor connects using HTTP, Varnish returns the cached content with HTTPS links and the visitor loads CSS/JavaScript over HTTPS. If the user clicks any links to other content on the site, they access that content over HTTPS.

However, if we reverse the situation (that is, the first visitor uses HTTP and the second visitor uses HTTPS), a problem arises for the second visitor: the cached content instructs the visitor to load the CSS/JavaScript over HTTP. Browsers refuse to do this for security reasons, the browser shows an "insecure content" warning, and none of the formatting/scripts work.

Attempted Solutions:

  1. Disable SSL entirely, including for the admin interface. This is the least preferred option, but at least it keeps things working consistently (albeit insecurely).
  2. Redirect all non-admin traffic to HTTP with the following .htaccess rule:

    RewriteCond %{HTTP:X-Forwarded-Proto} =https
    RewriteRule !^wp-(admin|login|register)(.*) - [C]
    RewriteRule ^(.*)$ http://%{SERVER_NAME}/$1 [L]
    

    This would work if not for Varnish: if the visitor accesses a non-admin/login/register URL at the site over HTTPS, Apache does a rewrite to point the visitor at the HTTP version of the content. Varnish caches the redirect without caring about which protocol is used, so that visitor and all subsequent visitors get stuck in a rewrite loop. Even if it worked, this method is less than ideal because it forces all non-admin users to access the site over HTTP only -- I want to allow users to be able to access the content using HTTP or HTTPS, at their choice.

Question: Without having admin access to the Varnish+SSL system, is there some way of resolving this problem so that users accessing the site over HTTP and HTTPS don't tread on each other's caches?

I know this is a rather old question but I'll leave my findings in case someone else needs to make this work.

Your approach helped me implement a similar setup.

In order for Varnish to keep a separate cache for http and https versions of the same page you simply need to add the X-Forwarded-Proto header in the hash_data() function in vcl_hash.

Like this:

sub vcl_hash {
        if (req.http.host) {
                hash_data(req.http.host);
        } else {
                hash_data(server.ip);
        }

        # Keep separate caches if https is used
        hash_data(req.http.X-Forwarded-Proto);
}

This way a user visiting http://www.example.net will get the cache for the http version while a user visiting https://www.example.net will get the cache for the https version.

You may want to make it more specific so it will only keep double caches for the html content and not images, css, js, etc which are usually the same regardless of the protocol used.

  • +1 for the helpful explanation, but as a side note the OP problem is probably with "caching proxy as service" type of situation in which you have very limited control on the configuration of the various parts of the proxy. – Mark Kaplun May 5 '17 at 2:41
  • You are correct, I didn't notice the 'Without having admin access to the Varnish+SSL system' part of the question. – Cha0s May 5 '17 at 3:20

There should not be a problem unless you have some misconfiguration or misbehaving theme or plugin. Wordpress core sends an http header that instructs caching servers to not cache the content being sent whenever the page is accessed by a logged-in user.

For reference the headers are set at wp_get_nocache_headers and sent from send_headers, and from a 2 min code review right now the code seems very solid to me.

  • 1
    The issue is not with logged-in users (accessing the admin interface over HTTPS works fine) -- rather, it's the problem of when a user accesses the normal site content using HTTP. That works fine for them, but Varnish caches the HTTP URLs for CSS/JavaScript and serves them to HTTPS users in the future (so HTTPS users get insecure content warnings and broken rendering). The opposite also occurs, but this isn't a major problem other than a slight latency hit. – heypete Oct 5 '14 at 13:22
  • If you do not want to serve cached content over https you need to configure the varnish to do that. There is nothing you can do from wordpress about it.. – Mark Kaplun Jul 6 '16 at 18:12

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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