I'm trying to use the hls.js library for displaying Reddit hosted videos from the HLS/DASH playlist. However, accessing any Reddit HLS/DASH url, such as this one through XHR will fail due to, what the error says is a Same Origin Policy violation:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
Weird thing is, the request prior GET is an OPTIONS one, which does return the Access-Control-Allow-Origin header with the correct origin url. I can see the response in the developer console, but the request still "fails". If I use the "Allow CORS: Access-Control-Allow-Origin" extension, everything works fine. What am I doing incorrectly?
OPTIONS request headers:
Host: v.redd.it
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: */*
Accept-Language: lt,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Referer: http://localhost:3000/
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Save-Data: on
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers
OPTIONS response headers:
HTTP/2 200 OK
retry-after: 0
access-control-allow-origin: http://localhost:3000
access-control-allow-headers: authorization
access-control-allow-methods: GET
access-control-max-age: 3000
date: Sun, 08 Mar 2020 18:40:29 GMT
via: 1.1 varnish
x-served-by: cache-hhn4029-HHN
x-cache: HIT
x-cache-hits: 0
x-timer: S1583692829.044409,VS0,VE0
server: snooserv
accept-ranges: bytes
x-cdn-server-region: EU-East
x-cdn-client-region: EU
x-cdn-name: fastly
access-control-expose-headers: x-cdn-server-region, x-cdn-client-region, x-cdn-name
cache-control: public, max-age=604800, s-maxage=86400, must-revalidate
vary: Access-Control-Request-Headers, Access-Control-Request-Method,Origin
content-length: 0
X-Firefox-Spdy: h2
GET request headers:
Host: v.redd.it
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: */*
Accept-Language: lt,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:3000/
Authorization: Bearer --------------------------- (actual token here)
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Save-Data: on
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers
GET response headers:
HTTP/2 200 OK
last-modified: Sun, 08 Mar 2020 15:34:11 GMT
etag: "0abfb243cb8d03188bf34ff29d6d4af8"
content-type: application/dash+xml
via: 1.1 varnish
date: Sun, 08 Mar 2020 18:40:29 GMT
via: 1.1 varnish
x-served-by: cache-bwi5138-BWI, cache-hhn4029-HHN
x-cache: HIT, HIT
x-cache-hits: 2, 34
x-timer: S1583692829.127808,VS0,VE0
server: snooserv
accept-ranges: bytes
x-cdn-server-region: EU-East
x-cdn-client-region: EU
x-cdn-name: fastly
access-control-expose-headers: x-cdn-server-region, x-cdn-client-region, x-cdn-name
cache-control: public, max-age=604800, s-maxage=86400, must-revalidate
vary: Origin
content-length: 1976
X-Firefox-Spdy: h2
GET response:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MPD mediaPresentationDuration="PT15.034S" minBufferTime="PT1.500S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static" xmlns="urn:mpeg:dash:schema:mpd:2011">
<Period duration="PT15.034S">
<AdaptationSet segmentAlignment="true" subsegmentAlignment="true" subsegmentStartsWithSAP="1">
<Representation bandwidth="1172841" codecs="avc1.4d401f" frameRate="30" height="480" id="VIDEO-1" mimeType="video/mp4" startWithSAP="1" width="720">
<BaseURL>DASH_480</BaseURL>
<SegmentBase indexRange="913-992" indexRangeExact="true">
<Initialization range="0-912"/>
</SegmentBase>
</Representation>
<Representation bandwidth="784684" codecs="avc1.4d401e" frameRate="30" height="360" id="VIDEO-2" mimeType="video/mp4" startWithSAP="1" width="540">
<BaseURL>DASH_360</BaseURL>
<SegmentBase indexRange="915-994" indexRangeExact="true">
<Initialization range="0-914"/>
</SegmentBase>
</Representation>
<Representation bandwidth="592784" codecs="avc1.4d401e" frameRate="30" height="240" id="VIDEO-3" mimeType="video/mp4" startWithSAP="1" width="360">
<BaseURL>DASH_240</BaseURL>
<SegmentBase indexRange="915-994" indexRangeExact="true">
<Initialization range="0-914"/>
</SegmentBase>
</Representation>
<Representation bandwidth="91690" codecs="avc1.4d400a" frameRate="30" height="96" id="VIDEO-4" mimeType="video/mp4" startWithSAP="1" width="144">
<BaseURL>DASH_96</BaseURL>
<SegmentBase indexRange="912-991" indexRangeExact="true">
<Initialization range="0-911"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>
Thanks a lot for your help.
The problem is just: the response from https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd
to the GET
request from your code doesn’t include the Access-Control-Allow-Origin
response header. Strangely though, it does include the Access-Control-Expose-Headers
response header. So the cause is just that the server is misconfigured; it’s not actually properly CORS-enabled.
Specifically: even though the server sends a Access-Control-Allow-Origin
header in response to the OPTIONS
preflight, that on its own isn’t sufficient to make browsers allow your frontend code to access the response to the actual GET
request in your code. For your code to work, the server must also send the Access-Control-Allow-Origin
header in response to that GET
request too.
But you can actually work around the https://v.redd.it
misconfiguration and access Reddit HLS/DASH URLs from your frontend code without needing a browser extension — by making your request through CORS proxy, as in the following example.
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => console.log(contents))
For explanation, see the answer at https://stackoverflow.com/a/43881141/441757.
You can easily run your own proxy using code from https://github.com/Rob--W/cors-anywhere/, and you can quickly deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands:
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master
After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://cryptic-headland-94862.herokuapp.com/. So then rather than prefixing your request URL with https://cors-anywhere.herokuapp.com
, prefix it instead with the URL for your own instance; e.g., https://cryptic-headland-94862.herokuapp.com/https://example.com.