slop audit
Blog post 45 / Browser checks

Mixed content errors: find and fix HTTPS launch failures

Find mixed content errors on HTTPS pages, repair insecure asset and API URLs, verify browser behavior, and add focused checks before a website launch.

By Slop Audit · 8 min read

Mixed content errors happen when a page loaded securely over HTTPS tries to use an insecure resource, such as an HTTP script, stylesheet, image, or API endpoint. The homepage can load successfully while a blocked dependency leaves the form empty, removes a font, or stops the primary action. The W3C mixed content draft describes this relationship between a secure document and an insecure subresource.

For developers launching a student project, portfolio, or product website, the practical fix is to identify the insecure URL, correct the code or configuration that produces it, and retest the deployed task. Start with the failing resource rather than changing browser settings.

Mixed content errors checklist

  1. Open the exact deployed HTTPS route in a clean browser session.
  2. Reload with Console and Network open, preserving messages through navigation.
  3. Record the insecure resource URL and the code or document that requested it.
  4. Verify that the HTTPS replacement serves the right resource.
  5. Fix its source in templates, CSS, JavaScript, content data, or deployment configuration.
  6. Exercise the task that loads it, including lazy images and embedded content.
  7. Add a focused regression check and keep the release evidence.

A successful HTTPS document request only verifies that document. It does not establish that every dependency works. Keep this check alongside your website launch checklist.

Understand blocked and upgraded mixed content

Browsers handle insecure subresources differently. Scripts, stylesheets, fonts, frames, and fetch() requests can be blocked. Some image, audio, and video requests can be upgraded to HTTPS automatically. That upgrade still needs a working HTTPS endpoint.

For example, these dependencies on an HTTPS page need correction:

<link rel="stylesheet" href="http://assets.example.com/site.css">
<script src="http://assets.example.com/app.js" defer></script>
<img src="http://assets.example.com/logo.png" alt="Example project">

The stylesheet and script may never load. The image may appear because the browser upgraded its request, leaving the insecure source URL unnoticed during visual review. Responsive image URLs in srcset and picture require particular attention; do not assume they get the same upgrade treatment as a plain image src.

MDN's mixed content guide documents which requests browsers upgrade or block, including different treatment for resource types and IP-address hosts. Use the current browser evidence rather than older advice that assumes insecure images always load unchanged.

An ordinary link that navigates to an HTTP page in a top-level browsing context is a separate transport problem. Do not classify every http:// string as a mixed-content failure: prose examples, XML namespaces, and external navigation links need context. The W3C draft’s content categories distinguish top-level navigation from subresource loading. Downloads from HTTPS pages should also use secure destinations; they have their own browser restrictions.

Find the insecure request in DevTools

Open the deployed route and use the Console message to identify the insecure resource. Then use Network to trace its origin:

  • Enable Preserve log if the task navigates between pages.
  • Disable the browser cache while DevTools is open and reload.
  • Inspect the failing request, its URL, resource type, and blocked reason.
  • Open Initiator to find the stylesheet, script, or document that requested it.
  • Repeat the action that reveals the failure, such as opening a panel or loading an image farther down the page.

Chrome's Network reference describes request details, initiators, cache controls, and preserved logs. Record the production route, resource URL, initiating source, browser, action, and release identifier so another developer can reproduce the problem.

If no HTTP request reached the server, that can be the expected result of browser blocking. A server access log or curl request cannot reproduce every browser policy decision. Use a browser for the final verification.

Separate mixed content from other blocked requests

Read the full error before applying a fix:

  • Mixed content: a secure page attempted an insecure dependency. Correct the transport and URL source.
  • CORS: the browser rejected cross-origin access. Switching to HTTPS alone may leave a separate CORS configuration issue.
  • CSP: a policy disallowed a resource source. A secure URL can still violate script-src, connect-src, or another directive.
  • HTTP error: the server returned a response such as 404 or 500. Investigate routing, availability, or application behavior.

The JavaScript console errors guide covers wider runtime triage. For policy restrictions, use the website security headers checklist. Removing a CSP to silence a message does not repair an insecure dependency.

Fix mixed content at the URL source

First confirm the replacement is real. Open the exact HTTPS asset or endpoint and verify its certificate, response status, content type, and expected content. A hostname that supports HTTPS can still return an HTML fallback at a missing JavaScript path.

For assets served by your own application, a root-relative URL avoids hard-coding the scheme and host:

<link rel="stylesheet" href="/assets/site.css">
<script src="/assets/app.js" defer></script>
<img src="/assets/logo.png" alt="Example project">

These paths assume the application owns the origin root. If it is deployed below /portfolio/, use the framework's configured asset base so the files resolve under that prefix. For an external asset host, use an explicit HTTPS URL after testing that host.

Repair production API configuration

A common launch failure is a frontend built with an HTTP API base URL:

// Fails as an insecure dependency on the deployed HTTPS page.
const response = await fetch("http://api.example.com/projects");

If the application serves the API on the same origin, use its actual route:

const response = await fetch("/api/projects");
if (!response.ok) {
  throw new Error(`Projects request failed: ${response.status}`);
}
const projects = await response.json();

For a separate API origin, configure its tested HTTPS URL. Review the production environment variable, rebuild if your bundler embeds that value, and redeploy. Editing a local .env file does not change an already published JavaScript bundle.

Also inspect URLs generated behind a reverse proxy. The public request may be HTTPS while the application sees HTTP between the proxy and origin. Configure the framework and trusted proxy boundary correctly so generated asset URLs use the public scheme. Avoid trusting arbitrary client-supplied forwarding headers.

Check CSS, content data, and old bundles

The insecure URL may not appear in the page's initial HTML. Search the code and configuration you own:

rg -n 'http://' src public

Adjust the directories to your project. Review each match rather than replacing every string. Pay particular attention to CSS url() values, @font-face, CMS image fields, embed configuration, and API base URLs.

If the source is fixed but the deployed browser still requests the old URL, inspect the response and bundle version. Check CDN cache behavior, HTML asset references, and any service worker that serves cached files. Test both a fresh session and an existing browser profile before signing off. The image optimization checklist helps verify responsive sources and lazy-loading paths after the URL repair.

If a third-party dependency has no usable HTTPS endpoint, remove it, replace it, or use an approved architecture that serves the required content securely. Do not ask visitors to allow insecure content to complete your product's main task.

Use upgrade-insecure-requests during a migration

A site with many legacy URLs can use a CSP directive to upgrade insecure resource requests:

Content-Security-Policy: upgrade-insecure-requests

Add the directive to the policy your team owns rather than casually replacing an existing CSP. Verify every required endpoint over HTTPS before rollout. As MDN's upgrade-insecure-requests reference explains, an upgraded request has no HTTP fallback if the secure endpoint fails. The directive also does not replace HSTS for protecting top-level visits.

Treat it as a migration aid. Keep correcting templates and stored URLs so the application publishes secure references directly. A browser that silently upgrades an old URL can make a visual test pass while the stale reference remains in your content.

Add focused mixed content testing to Playwright

For a stable release path, keep a browser test that checks the resulting page state and retains network evidence. This example assumes an HTTPS deployment with /api/projects and a visible projects list; replace both with the contract your application owns:

import { expect, test } from "@playwright/test";

test("projects load securely on the deployed page", async ({ page }) => {
  const failures: string[] = [];

  page.on("requestfailed", (request) => {
    failures.push(
      `${request.url()} ${request.failure()?.errorText ?? "request failed"}`,
    );
  });

  const projectsResponse = page.waitForResponse((response) => {
    return new URL(response.url()).pathname === "/api/projects"
      && response.request().method() === "GET";
  });

  await page.goto("https://example.com/projects");
  const response = await projectsResponse;
  expect(new URL(response.url()).protocol).toBe("https:");
  expect(response.status()).toBe(200);
  await expect(page.getByRole("list", { name: "Projects" })).toBeVisible();
  expect(failures, failures.join("\n")).toEqual([]);
});

Register listeners and the response wait before navigation so an early request is captured. If the API request is blocked or never starts, the response wait times out. If it returns an error, the status assertion catches it. The page-state assertion checks that the task still produces a visible result.

Playwright's request lifecycle documentation distinguishes transport failures from HTTP responses: a 404 or 503 does not trigger requestfailed. Keep both signals when testing a required dependency.

This test catches failures on one path; it does not detect every insecure source URL. Automatically upgraded images may succeed. Review Console warnings and stored asset references separately, and add application-specific image or interaction assertions for important lazy content. Keep expected third-party failures narrowly documented instead of suppressing all network failures. The website regression testing guide explains how to retain a reproducible baseline.

Check the deployed page with Slop Audit

After fixing the URLs, run a Slop Audit on the public HTTPS route. The report checks the final URL's HTTPS scheme and, when the bounded browser stage completes, records error-level console messages, uncaught page errors, and failed same-site requests. Use the observed message, source URL, and viewport to investigate a remaining load failure, then rescan after the repair.

Slop Audit does not have a dedicated mixed-content detector. Its runtime findings filter third-party source URLs, and it does not collect every warning, inspect all insecure references, or exercise every interaction. An automatically upgraded resource can work without appearing as a runtime failure. Keep the focused browser test and manual URL review above as part of your release evidence.

Before sharing the launch URL, save the failing resource, its corrected source, the HTTPS response, and the successful browser task. That gives the next developer a concrete regression check instead of a vague instruction to make the site secure.