Fixing mixed content issues

Table of contents

Most web developers are surprised the first time they see a browser warning about "mixed content" pop up. While the website itself is securely loaded over HTTPS, a single resource being loaded without encryption can undermine the protection of using this protection in the first place.

What is mixed content?

Mixed content occurs when a website is loaded over a secure HTTPS connection, but some of the resources (like images, scripts, or stylesheets) are loaded over an insecure HTTP connection. The problem with even a single unencrypted request is that it compromises the encryption as a whole. When thinking about HTTP traffic, we typically think about a browser sending a request to a server, like this:

But inbetween these two are numerous servers that handle and route the request (called "routers" or "hops"), that forward the message to the server, or another router if it cant reach the server directly, so in reality it looks more like this:

HTTPS traffic encrypts the request in the browser in such a way that only the server at the other end can encrypt it. The routers inbetween can forward the message, but cannot read or change it, as it is encrypted for them. If an attacker compromised one of the routers, they could read all the requests and responses between the browser and server that aren't encrypted, but if they are encrypted they can only see that data is transmitted, not what data. They can also not change the response from the server to the browser, as they would need to decrypt it first, change it, and then re-encrypt it with the same key.

Mixed content is divided into active and passive mixed content:


  • Passive Mixed Content: Includes resources like images, videos, and audio files. Although these resources don’t interact directly with the website’s functionality, they can still leak sensitive information such as cookies. This can lead to session stealing where attackers hijack user sessions.

  • Active Mixed Content: Includes resources like scripts, iframes, and stylesheets. These are more dangerous because they can be manipulated by attackers to modify the content and behavior of a website. Attackers can inject malicious code, steal data, or redirect users to malicious sites by altering the response from the server.

Fixing URLs

To fix mixed content issues, you need to ensure that all resources on your website are loaded over HTTPS. This can be done in one of two ways.

Firstly, you could simply change all http:// urls to https:// urls:

While this does solve the mixed content issue, it has two drawbacks: when changing the domain of the website, you need to edit all links again, and writing the domain and protocol into ever link takes up characters that don't need to be transferred to the client.

A better approach would be to use relative URLs instead, simply by removing the protocol and domain and only using the path:

Now the link will always use the same protocol and domain as the HTML document that contains it, so if the website is accessed through https://my.example.com, then the link will behave like https://my.example.com/img/dog.jpg.

Enforcing HTTPS Through the Server

For a short-term fix, especially if your website has many legacy http:// links, you can use a Content Security Policy (CSP) to upgrade insecure requests to HTTPS while you fix the links. You can configure your web server to send the following header:

Content-Security-Policy: upgrade-insecure-requests;

This will instruct the browser of any visitors to treat http:// links as https:// instead.


To add the header in apache2, add the Header directive to your virtualhost:

<VirtualHost *:443>
    ...
    Header always set Content-Security-Policy "upgrade-insecure-requests;"
    ...
</VirtualHost>

For nginx, the add_header directive in the corresponding server block has the same effect:

server {
    listen 443 ssl;
    ...
    add_header Content-Security-Policy "upgrade-insecure-requests;";
    ...
}

Make sure to reload or restart the web server daemon after saving the configuration file to apply the changes.

More articles

Scheduled background tasks with cron

Getting things done automatically

Setting up an nginx load balancer

Distributing requests to multiple servers

Simplifying terminal operations with python modules

Saving time with python builtin modules

Modern linux networking basics

Getting started with systemd-networkd, NetworkManager and the iproute2 suite

Understanding how RAID 5 works

Striking a balance between fault tolerance, speed and usable disk space

A practical guide to filesystems

Picking the best option to format your next drive