angular.io preloading hints are misconfigured causing duplicate requests
See original GitHub issueπ Docs or angular.io bug report
Description
See duplicate requests in the network panel + warnings in the console:

π¬ Minimal Reproduction
visit angular.io or next.angular.io
Whatβs the affected URL?**
Reproduction Steps**
Open dev console to see the warnings
Expected vs Actual Behavior**
no warnings, no duplicate requests
π₯ Exception or Error
VM17:1 A preload for 'https://next.angular.io/generated/navigation.json' is found, but is not used because the request headers do not match.
(anonymous) @ VM17:1
VM17:1 A preload for 'https://next.angular.io/generated/docs/index.json' is found, but is not used because the request headers do not match.
(anonymous) @ VM17:1
The resource https://next.angular.io/generated/navigation.json was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally.
The resource https://next.angular.io/generated/docs/index.json was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally.
π Your Environment
Browser info
Anything else relevant?
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (6 by maintainers)
Top Results From Across the Web
Duplicate request is always performed when ... - Stack Overflow
The function this.handleError() will be called only once. Which means that the duplicate request is not really taken and processed. Check theΒ ...
Read more >Pitfalls and Common Mistakes | NGINX
The most frequent issue we see happens when someone attempts to just copy and paste a configuration snippet from some other guide. Not...
Read more >Angular: How to prevent duplicated HTTP requests
Every time we call it, we'll make a new HTTP request to the server. A first solution is simply caching the response: 1....
Read more >X-Content-Type-Options - HTTP - MDN Web Docs - Mozilla
Blocks a request if the request destination is of type style and the MIME type is not text/css , or of type script...
Read more >Safari Technology Preview Release Notes - Apple Developer
Changed to not route the navigation preload response body to the service worker ... Fixed fetch event handling delays caused by other JavaScript...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
I talked to a few people from Chrome and this seems to be a spec bug / oversight / missing feature, so I filed an issue at https://github.com/w3c/preload/issues/157.
It will most likely take a while to get this resolved and implemented in all browsers, so we might want to consider other options including changing the default as discussed in #28915.
I looked into this and I couldnβt figure out a good way to fix it. Feels like a browser limitation (or even bug?) - see below.
Here is my journey/findings:
Since the warning mentions that the problem is the different request headers, I started by comparing the headers of the preload request with that of the actual, in-app request.
The first request header that was different was
Accept
. It had a value of*/*
in the preload request and a value ofapplication/json, text/plain, */*
in the actual request. This is because theHttpClient
will explicitly setAccept
if not specified by the user:https://github.com/angular/angular/blob/4ce743dfb8b89b54093c4c9300ca24c5d1f50544/packages/common/http/src/xhr.ts#L71-L74
To work around this issue, I explicitly specified
Accept: */*
inHttpClient#get()
request here:Now the
Accept
headers appeared identical for the two requests in the Network tab in DevTools, but I still got the warning in the Console. So, I looked for other differences in headers.Spoiler
The `Accept` header is the problem. But I didn't find that out until later.The next header that was different was
Sec-Fetch-Mode
. It had a value ofno-cors
in the preload request and a value ofcors
in the actual request. Addingcrossorigin
to the preload request (as suggested anyway in some resources, including MDN) fixes theSec-Fetch-Mode
discrepancy, butβ¦Adding
crossorigin
to the preload request causes it to have anOrigin
header, which is missing from the actual, in-app request. At this point, I thought this difference was causing the problem and I couldnβt figure out a way to force the actual request to have that header too.At this point, I tried replacing
HttpClient#get()
with a nativefetch()
orXMLHttpRequest
and sure enough the warning went away (i.e. the preloadednavigation.json
file was re-used for the actual, in-app request and no new request was made).Long story short, it turns out that explicitly setting the
Accept
header causes the browser to consider the request headers different and not use the preloaded file. Confusingly, this happens even if theAccept
header is set to the same value that is shown in the Network tab for the preload request, namely*/*
π€·ββοΈSo, basically, replacing this line with any of the following methods fixes the issue:
Working solution 1 - Using
fetch()
:Working solution 2 - Using
XMLHttpRequest
:NOTE: This is essentially what
HttpClient#get()
does under hood with the exception thatHttpClient#get()
will also callxhr.setRequestHeader('Accept', 'application/json, text/plain, */*')
.Similarly, the following variations of the above solutions stop to work as soon as you try to set the
Accept
header:Non-working solution 1 - Using
fetch()
(with theAccept
header):Non-working solution 2 - Using
XMLHttpRequest
(with theAccept
header):NOTE: This is essentially exactly what
HttpClient#get()
does under hood for this request.BONUS
Hacky working solution tricking
HttpClient
into not adding theAccept
header: