In an Angular 9 app, when I add this image caching snippet to ngsw-config.json, I get a CORS error and images don't display. If I remove this snippet the app works correctly (with no errors).
Access to fetch at 'https://assets.myurl.net/images/banner.jpg' from origin 'https://localhost:8100' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
ngsw-config.json (snippet):
"assetGroups": [{
"name": "cdn",
"installMode": "lazy",
"updateMode": "lazy",
"resources": {
"urls": [
"https://assets.myurl.net/**"
]
}
}]
Images are stored in an AWS S3 bucket with CORS configuration:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
</CORSRule>
</CORSConfiguration>
- Cloudflare is being used as a CDN (and DNS).
- The app is on the latest 9.x releases: @angular 9.2.1, @angular/service-worker 9.1.12.
- Adding crossorigin="anonymous" to img and picture tags didn't seem to help.
Steps to consistently reproduce the error in Chrome/PC:
- Clear browser cache
- Open browser private/incognito
- Go to website -- images show okay
- Click refresh -- images no longer show - CORS errors in browser console
UPDATE
Based on this post (S3 not returning Access-Control-Allow-Origin headers?) the issue appears to be that the service worker does not send an Origin in the request header (so S3 doesn't send access-control headers).
Service Worker Request (copied from Chrome console):
Request URL: https://assets.myurl.net/images/banner.jpg
Referrer Policy: strict-origin-when-cross-origin
Provisional headers are shown
Referer: https://localhost:8100/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
First of all, make sure an Origin header with every request. If no Origin header is sent, S3 won't send access-control headers, as S3 deems them irrelevant (and typically, they are). A browser (for which the CORS mechanism is meant) will automatically send an Origin header when doing cross-origin HTTP requests through XMLHTTPRequest.
Based on this post (S3 - Access-Control-Allow-Origin Header) the S3 cors script has been updated to include:
<AllowedMethod>HEAD</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
Adding crossorigin="anonymous" to img tags doesn't change the service worker request header. So this doesn't fix the problem.
Cloudflare cache has been purged.
This obscure post (https://community.cloudflare.com/t/access-control-allow-origin-headers-stripped/158102/2) includes:
If you add or change CORS configuration at your origin web server, purging the Cloudflare cache by URL does NOT update the CORS headers.
I cache busted the banner.jpg image by adding a random querystring (i.e. banner.jpg?q=abc). The banner image always displays correctly (while the other images still show cors errors). The service worker cache in Chrome console Application Tab > Cache Storage shows that the image as cached. Cloudflare caching configuration "Caching Level" = Standard "delivers a different resource each time the query string changes".
However, if I upload a new image to S3 banner01.jpg (i.e. not already cached by Cloudflare) I do get a cors error showing this image. I tried this three additional times by uploading different image names and did not get a cors error. Interestingly, an hour later even the new images are showing cors errors.
A curl command (that does not specify Origin in header) runs successfully on my local PC. The response does not contain CORS headers. e.g.
curl -v "https://assets.myurl.net/images/banner.jpg"
Adding origin to the curl command does return CORS headers. e.g.
curl -v --header "Origin: https://www.example.com" "https://assets.myurl.net/images/banner.jpg"
access-control-allow-origin: https://www.example.com
access-control-allow-methods: GET, HEAD
access-control-allow-credentials: true
UPDATE 2
I can't explain why, but Chrome now returns cors headers in the Angular Service Worker fetch response. The request does NOT include an Origin header.
accept-ranges: bytes
access-control-allow-methods: GET, HEAD
access-control-allow-origin: *
However, Safari does not. All browsers appear to be working okay but Safari still shows cors errors.