Upgrade-Insecure-Requests: the request header vs the CSP directive (tested)
Two different things share this name: a request header Chrome sends, and a CSP directive your server sends. We logged every request to see what each one does.
1. The request header: Upgrade-Insecure-Requests: 1
Chrome sent Upgrade-Insecure-Requests: 1 on every page navigation (HTTP and HTTPS pages alike) and on none of the image, script or favicon requests. It means "this browser prefers HTTPS; you may redirect me". You don't set it, and seeing it in your logs is normal. A server may answer an HTTP request carrying it with a redirect to HTTPS (HSTS does this job better).
2. The CSP directive: what got upgraded
The page's HTML referenced http:// URLs; this is the scheme the request actually used:
| Page | CSP | http:// image | http:// script | Mixed-content warnings |
|---|---|---|---|---|
| HTTP | none | HTTP | HTTP | - |
| HTTP | upgrade-insecure-requests | HTTPS | HTTPS | - |
| HTTPS | none | HTTPS (Chrome auto-upgrades images) | Blocked, never requested | Yes, one per resource |
| HTTPS | upgrade-insecure-requests | HTTPS | HTTPS, ran | None |
- The upgrade only changes the scheme.
http://img.test:8996/becamehttps://img.test:8996/, same port (the spec maps port 80 to 443). - It breaks anything without HTTPS. Upgraded requests to the plain-HTTP-only host failed with
net::ERR_SSL_PROTOCOL_ERROR; there is no fallback to HTTP. Check that every third-party host you embed serves HTTPS first. - On an HTTPS page it is the quick fix for old
http://script/CSS/iframe URLs in content you can't easily rewrite (CMS posts, legacy templates): blocked scripts ran after adding it.
Content-Security-Policy: upgrade-insecure-requests
# nginx
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# HTML (works as a meta tag too)
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
If you already send a CSP, add it as one more directive, e.g. default-src 'self'; upgrade-insecure-requests (CSP header examples). In Content-Security-Policy-Report-Only it did nothing; Chrome logs "The Content Security Policy directive 'upgrade-insecure-requests' is ignored when delivered in a report-only policy." The <meta> version worked like the header in our test. Build the full set in the free security headers generator.
Frequently asked questions
What does Upgrade-Insecure-Requests: 1 mean?
It is a request header the browser sends on page navigations to say it prefers HTTPS. In our Chrome test it was sent on navigations only, never on images or scripts.
Do I need to set the Upgrade-Insecure-Requests header?
No. The browser sends the request header. What you can set is the CSP directive upgrade-insecure-requests in a Content-Security-Policy response header or meta tag.
Does upgrade-insecure-requests fix mixed content?
Yes for resources whose host serves HTTPS: in our test an http:// script blocked as mixed content loaded over HTTPS and ran once the directive was added. Hosts without HTTPS fail instead (ERR_SSL_PROTOCOL_ERROR).
Does upgrade-insecure-requests change the port?
No, only the scheme; http://host:8996 became https://host:8996. Per the spec port 80 maps to 443.
Is upgrade-insecure-requests a replacement for HSTS?
No. It rewrites subresource URLs inside your pages; HSTS makes the browser use HTTPS for your whole domain.
Written with AI assistance. Measured on 2026-10-01 in Chromium 153 (full build), fresh context per row, non-loopback test hostnames (site.test / img.test mapped with --host-resolver-rules, self-signed certificate accepted): an HTTP page and an HTTPS page each loaded http:// images and scripts from a port that only speaks HTTPS and from a port that only speaks plain HTTP, with and without Content-Security-Policy: upgrade-insecure-requests (also as a <meta> tag and as Report-Only on the HTTPS page); the servers logged the scheme and request headers of every request. Link-navigation upgrades and the port-80→443 rule are from the spec, not this test. PlainHeaders is our extension; the other methods are free.