Chrome 44 Sending HTTPs Header By Mistake, Breaking (Some) Web Applications

Mattias Geniar, Thursday, July 23, 2015 - last modified: Saturday, August 1, 2015

Update #1: it's less worse than it looked, see details below.
Update #2: chrome got updated, the HTTPS-header is gone, see details below.

Now this is interesting.

In the Chrome 44 release (version 44.0.2403.89) that happened just yesterday, it appears the browser got a small bug significant change.

It's now sending the HTTPS: 1 header on every request by default. This was probably meant as a security improvement, to suggest HTTPs to the server wherever possible, but it's breaking WordPress and other webserver installations all over the place.


Why? Because most PHP software uses $_SERVER['HTTPS']; to detect if the site is running behind an SSL certificate or not. This includes WordPress, Drupal and any custom PHP software that checks this header.

  // Assume HTTPs, redirect or enable https:// prefixes on all resources (css/js/images/...)

The next planned release of Chrome is scheduled on July 27th, but they're investigating if an emergency patch can be sent out to resolve this issue.

Bugtracker: Issue 505268: Forcing WordPress sites to use https even when not directed.

This is not going to be a fun week for Chrome users.

Update #1: only HTTP_ prefixed headers are affected.

Any request header a browser sends along, gets prefixed with HTTP_ in the $_SERVER global variable. The User-Agent gets transformed into HTTP_USER_AGENT, the Accept-header gets turned into HTTP_ACCEPT, ... and so on.

Here's what those HTTP headers look like according to PHP.


    [HTTPS] => on
    [HTTP_HOST] =>
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    [HTTP_ACCEPT_ENCODING] => gzip, deflate, sdch
    [HTTP_ACCEPT_LANGUAGE] => nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4,fr;q=0.2,it;q=0.2
    [HTTP_COOKIE] => some=value
    [HTTP_HTTPS] => 1
    [HTTP_USER_AGENT] => Mozilla/5.0

The first HTTPS value in the $_SERVER variable is the one set by the webserver, to indicate HTTPs or not. The second one, HTTP_HTTPS, is the HTTPS-header sent by the Chrome browser that gets transformed into an variable for PHP.

Some PHP code, like the WooCommerce WordPress plugin didn't only check $_SERVER['HTTPS'], but also looked at $_SERVER['HTTP_HTTPS'] to detect HTTPS on the site. This is wrong PHP code and has nothing to do with "bad PHP by design".

The HTTP_ prefix handling done by WooCommerce is probably done as a result of a Apache bug/feature that once existed, where after 301/302 HTTP rewrites some environment variables would always get prefixed with another HTTP_ keyword.

There are also reverse proxy configs (Nginx, Varnish, ...) out there that also pass environment variables along that get interpreted by Apache as headers and thus receive the HTTP_ prefix.

Either way, most plugins have had an update by now, just go to your WordPress/Drupal/... site and update all plugins. Chances are, the problems will be gone, even if Chrome takes a couple more days to get the patch out.

Fixing WooCommerce specifically

Most problems are reported by WooCommerce, the WordPress plugin. Since chances are, you can't log into your WordPress any more, you'll need another fix. Many thanks to Rahul Lohakare in the comments for this fix.

Modify the following file, either download it via FTP and upload it again or modify it directly on the server: wp-content\plugins\woocommerce\woocommerce.php

Comment out these lines:

if ( ! isset( $_SERVER['HTTPS'] ) && ! empty( $_SERVER['HTTP_HTTPS'] ) ) {

Once those have been commented (just prefix every line with #), WooCommerce/WordPress won't redirect you anymore.

If that still doesn't work, use Firefox or another browser to log into your site and update the plugin via the official WordPress update method. Since the problem is Chrome-only, you can bypass the redirect by using any other browser, temporarily.

Update #2: Chrome got updated, replaced 'HTTPS' with 'upgrade-insecure-requests'

Chrome pushed out an update, everyone should get it in a few hours. I'm now on version 44.0.2403.107, anything higher will not have this problem anymore.


After the update, the HTTPS header has been removed by the upgrade-insecure-request header.


Good news for everyone!

Hi! My name is Mattias Geniar. I'm a Support Manager at Nucleus Hosting in Belgium, a general web geek & public speaker. Currently working on DNS Spy & Oh Dear!. Follow me on Twitter as @mattiasgeniar.

Share this post

Did you like this post? Will you help me share it on social media? Thanks!


Tim Thursday, July 23, 2015 at 17:05 - Reply

To be fair, the underlying reason is just another bad design decision on PHP’s part.

    Sameer Thursday, July 23, 2015 at 17:10 - Reply

    It’s not a bad PHP design decision, it’s a bad design decision of whoever is using it that way in PHP. To be fair that code is actually only reading a HTTP header variable, so its not bad or good, its up to you how you wish to use it. Depending on it and assuming HTTPS is incorect.

      Navarr Thursday, July 23, 2015 at 17:22 - Reply

      No.. no, this is a PHP problem if that header is overwriting $_SERVER[‘HTTPS’].

      per the docs:

      > ‘HTTPS’
      > Set to a non-empty value if the script was queried through the HTTPS protocol.

        Mattias Geniar Thursday, July 23, 2015 at 20:30 - Reply

        Well, to be fair, PHP is inheriting the browser request headers but is indeed prefixing them with HTTP_*.

        However, some code translates HTTP_HTTPS to HTTPS and merges/overwrites them in the $_SERVER array. Not exactly a fault of PHP, the language, more of the framework processing all those values (which is usually put through some kind of validator to catch malicious input).

          Olivier Thursday, July 23, 2015 at 22:56 - Reply

          @Tim and you are both trolls who doesn’t even know PHP. Why do you spread lies?
          Go back using your “perfect” language which doesn’t have a single problem.

          The only websites affected by the problem explained in this article are websites horribly coded. I can’t understand a reason to check for the “HTTP_HTTPS” header.

            Tim Friday, July 24, 2015 at 09:03 - Reply

            Pointing out an obvious flaw doesn’t make one a troll. The fact of the matter is that there are plenty of ways to handle this better, and plenty of platforms that do. It’s just that PHP has an inclination to select the suboptimal approach.

              Olivier Friday, July 24, 2015 at 15:13 -

              Why don’t you want to understand the problem isn’t the PHP language but just a specific piece of code from “WooCommerce”.
              If you continue to say this problem is because of the language, you’re just trolling.

              Tim Friday, July 24, 2015 at 20:48 -

              My point is that having a CGI-style object that contains an amalgamation of various concepts is bound to result in issues like this one. A sanely designed language or framework will just let you call a function or inspect a clearly defined object to get just the HTTP header(s) that you’re looking for. Back when I was a PHP developer, I would run into obscure design decisions like these all the time, and it’s therefore my opinion that the core language is just broken. You don’t have to agree with me, but your gratuitous namecalling isn’t going to help anyone.

      Tim Thursday, July 23, 2015 at 17:27 - Reply

      You can’t seriously think having an associative array that contains all of, and I quote, “headers, paths, and script locations” is a good idea.

Glen Scott Thursday, July 23, 2015 at 17:28 - Reply

Are you sure $_SERVER['HTTPS'] can be populated from request headers? If it is sent by the browser, I believe it would be set as an HTTP_* index:


As per the documentation (

Set to a non-empty value if the script was queried through the HTTPS protocol.

Cash Williams Thursday, July 23, 2015 at 17:39 - Reply

I’ve checked a number of Drupal sites on a variety of hosting platforms, and from what I can tell Drupal is not effected.

Drupal checks for the value of ‘on’ rather then just a bool value of $_SERVER[‘HTTPS’]

735:  $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';

Stefan Thursday, July 23, 2015 at 18:39 - Reply

Hi Guys, here’s the plugin that fixes this for you.

Hope it will help you!

Gap Thursday, July 23, 2015 at 19:43 - Reply

You should enable

and disable

    Olivier Thursday, July 23, 2015 at 22:50 - Reply

    @Gap: do you work for NSA and try to spy on users more easily?
    Why do you suggest enabling crappy ciphers and disabling good ones?

    The only important thing to fix, is to generate a bigger Diffie-Hellman parameter.
    Well, OK, removing “TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)” doesn’t hurt.

    Mattias Geniar Friday, July 24, 2015 at 12:16 - Reply

    Hi Gap,

    Thanks for the heads-up, I’ve updated my SSL configs and I’m back to an A-rating, instead of the B.

    Much appreciated!

Trouble Thursday, July 23, 2015 at 19:47 - Reply

I don’t know much about web stuff, but as far as I can tell, “beta” means you definitely shouldn’t expect them to work? (Compare with “release”, which seems to mean “probably won’t catch fire”).

Lucinda Brown Friday, July 24, 2015 at 05:12 - Reply

Stefan’s plugin worked. Thank you so much. I bought you a beer.

    VlooMan Friday, July 24, 2015 at 11:24 - Reply

    Stefan’s plugin is basically a rude copy of the originally released plugin by filled with a forced donation notice. He didn’t even try to make it better.

    We just released a condition to ensure the “fix” is applied only to the affected pages.

    The Original Plugin and its code without unnecessary garbage can be found on GitHub with a detailed explanation:


Kelvin Friday, July 24, 2015 at 09:54 - Reply

How did you get the plugin to work!?… One needs to be able to sign in to WordPress to enable it – and the problem is that one cannot sign in as Google Chrome redirects to https and therefore the error message appears:
SSL connection error
Hide details
Unable to make a secure connection to the server. This may be a problem with the server, or it may be requiring a client authentication certificate that you don’t have.

Moreno Friday, July 24, 2015 at 11:19 - Reply

Thanks for the post, I have been contacting my hosting provider for my site which seemed to intermittently break, and they eventually pointed me here.

It would interesting to know if there’s a fix for WordPress that can be applied to patch the problem…

    Simon Archer Friday, July 24, 2015 at 11:34 - Reply

    I think you just need to upgrade your version of WooCommere. Version 2.3.13 removes the offending code. You can also add the line $_SERVER[‘HTTPS’] = ”; to your wp-config.php if you don’t have SSL enabled on your server.

VlooMan Friday, July 24, 2015 at 11:23 - Reply

Stefan’s plugin is basically a rude copy of the originally released plugin by filled with a forced donation notice. He didn’t even try to make it better.

We just released a condition to ensure the “FIX” is applied only to the affected pages.

The Original Plugin and its code without unnecessary garbage can be found on GitHub with a detailed explanation:


Simon Archer Friday, July 24, 2015 at 11:30 - Reply

I came across this myself. Is it actually a Chrome bug? It’s only affecting the $_SERVER[‘HTTP_HTTPS’] variable, and not the HTTPS one. Is Chrome effectively querying if there’s SSL enabled so it can redirect there if so?

I’m totally not clear on what the HTTP_HTTPS variable is for, but given it’s a different value from HTTPS, surely it serves a different purpose? This is bad form of the WooCommerce guys to add such a ‘fix’ without fully understanding what they’re fixing.

David Morles Friday, July 24, 2015 at 12:02 - Reply

Thank you for the information.

I solved this upgrading WooCommerce to version 2.3.13.

Rahul Lohakare Friday, July 24, 2015 at 14:23 - Reply

Hi All,

I have a solution for this problem. If you are wordpress user and if you have used woocommerse plugin then this problem will occur.
To solve this problem just go to wp-content\plugins\woocommerce\woocommerce.php and comment this

if ( ! isset( $_SERVER['HTTPS'] ) && ! empty( $_SERVER['HTTP_HTTPS'] ) ) {

around line 473.

Here you go….

mainoko Friday, July 24, 2015 at 16:31 - Reply

Ver.2.3.8 use
a lot of arrangement
can not woocommerce plugin update…:(

Want a list of PHP should be replaced

Rob W. Friday, July 24, 2015 at 18:01 - Reply

Thanks for posting this.
I couldn’t enter one of my websites and started stressing (I was using Chrome).

The quickest solution I found (without having to play with the PHP code) was to open up SAFARI, enter the admin section of the site and update the WooCommerce and any other plugins there.

Cheers for letting me know it wasn’t just my site with problems.
Now I can let out a deep sigh of relief.

Andrew Revinsky Friday, July 24, 2015 at 19:15 - Reply

@Mattias, is it possible for you to mark this post with a timestamp? It was not hard to determine the news is fresh.. Yet, it would be helpful.

Thank you for the article!!

dario Friday, July 24, 2015 at 20:38 - Reply

Did they fix it? I updated one of my Woocommerce sites, and before i could updted the rest, they started working again. Weird?

Otto Friday, July 24, 2015 at 21:03 - Reply

Ultimately, this is a Chrome misfeature in many ways. The thing is that different servers and sets of configurations behave differently, and so PHP apps that have to be portable have little tricks like these to handle output correctly.

The way it is supposed to be is that PHP will set $_SERVER[‘HTTPS’] to “on” if the incoming request is an https request. Simple.

However, a lot of people don’t actually run https directly on the server. They use a proxy server to do all the SSL bits. So while the site is actually serving over plain http, all the generated links still need to be https, because that’s how the browser is talking to the proxy server. This is not an uncommon setup, even CloudFlare can do this for you with their SSL offering.

Any header sent by a request to a server gets HTTP_ prefixed in front of it and passed in through the $_SERVER variable. Historically, proxy servers that are receiving https connections pass that along that fact in their request to the server. This header has invariably simply been called “HTTPS”, for obvious reasons.

So, the working standard is that if you receive a request header with HTTPS, then that’s a proxy server telling you that it’s relaying your response, whatever the hell it may be, back to the browser over an HTTPS connection. So, you, the webapp, should generate https: links in your content. The way this is detected is simple: $_SERVER[‘HTTP_HTTPS’].

Now, Chrome comes along and implements this without actually looking at all the possible scenarios. When Chrome, a browser, sets an HTTPS: 1 request header, then it’s triggering a lot of that same code. So, the PHP apps dutifully do what they have always done, and generate https: links in their output. Naturally, if your server doesn’t actually do https or have an SSL-certificate, then this is a problem. Site broken. Thanks Chrome.

It’s a misfeature that should not have made it into Chrome. Yes, it can be fixed by the various webapps in question, but this has been the standard case for years already. There is a notable difference for a webapp to be told that it’s actually communicating on an HTTPS connection vs. whether it is actually on a normal HTTP connection going through an HTTPS proxy. The HTTP_HTTPS variable has been used for this by basically everybody for many moons. If you set up an nginx ssl-proxy in the documented ways, this is how you do it.

So Chrome breaking that defacto situation is definitely Chrome’s fault. Quick fixes can be made by webapps, but the bottom line is that they should not have to do so. Chrome needs to stop doing that.

Waffleman Friday, July 24, 2015 at 21:12 - Reply

Thank you very much for this, Mattias.

You’re a real lifesaver!


Rodrigo Garcia Saturday, July 25, 2015 at 00:27 - Reply

Just to be clear, in my installation of Woo, I have the following:

if ( ! isset( $_SERVER['HTTPS'] ) ) {
       if ( ! empty( $_SERVER['HTTP_HTTPS'] ) ) {
               $_SERVER['HTTPS'] = $_SERVER['HTTP_HTTPS'];
       } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
               $_SERVER['HTTPS'] = '1';

I understand that we have to comment out the if statement. Im guessing we should then address the left over elseif statement. PHP will fail otherwise.

leaving the following uncommented and slightly modified with if instead of elseif:

if ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
        $_SERVER['HTTPS'] = '1';       

jay Saturday, July 25, 2015 at 02:52 - Reply

Anything using the https() function of perl’s is also affected

Mike Gill Tuesday, August 11, 2015 at 01:16 - Reply

The issue is still happening on devices where the certificate is not installed or when accessing a device over https like a firewall or appliance. For example if I try to access a Fortinet firewall internal to a network that the cert is not installed by IP address it returns the same net::ERR_INSECURE_RESPONSE error when trying to load the .js pages. This make managing the firewall in chrome IMPOSSIBLE unless you have the cert installed and access it by name. A good deal of companies do not always purchase certs for internal device management or install a cert server. So in trying to manage any of these devices in chrome is hit or miss, sometimes it works for a few minutes then crashes with the “sad face” or sometimes it fails as soon as you log into the device.

rlamrod Wednesday, February 8, 2017 at 07:18 - Reply

How can resolve in java application.

I have an application that receives HTTP/HTTPs requests on Apache Web Server and passes it on to tomcat.

Recently, I’ve been facing problems with sessions and redirection loops on Google Chrome and Firefox browsers after some user logs out the expiration of sessions. I managed to trace down the problem and found that the browser is not clearing the JSESSIONID cookie. After manually clearing that, it starts to work again.

I have checked the log files of Apache Web Server and I found this: – – [06/Feb/2017:05:23:27 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:29 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:29 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:29 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:30 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:30 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:30 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:31 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:31 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:31 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:32 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:32 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:32 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:34 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:34 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:34 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:35 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:23:35 -0600] “GET /preview/login.admin HTTP/1.1” 302 20 – – [06/Feb/2017:05:24:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20

Are the internet standards changing? Nonetheless, how can I fix this?

Leave a Reply

Your email address will not be published. Required fields are marked *

Inbound links