Chrome CORS error on request to localhost dev server from remote site
On Friday I had a working dev environment. On Monday I had a broken one. I encountered this error message in the Chrome dev-tools console for all my assets:
Access to CSS stylesheet at 'http://localhost:8080/build/app.css' from origin 'http://example.com' has been blocked by CORS policy: The request client is not a secure context and the resource is in more-private adress space
local
.
Is there any quick fix for this? I tried setting access-control-allow-origin in my webpack devServer.headers
config to no avail:
config.devServer.headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
}
Solution 1:
Original Answer
I finally found the answer, in this RFC about CORS-RFC1918 from a Chrome-team member. To sum it up, Chrome has implemented CORS-RFC1918, which prevents public network resources from requesting private-network resources - unless the public-network resource is secure (HTTPS) and the private-network resource provides appropriate (yet-undefined) CORS headers.
There's also a Chrome flag you can change to disable the new behavior for now:
chrome://flags/#block-insecure-private-network-requests
Disabling that flag does mean you're re-opening the security hole that Chrome's new behavior is meant to close.
Update 2021: A few months after I posted this question, the flag I referenced in my original answer was removed, and instead of disabling a security feature I was forced to solve the problem more satisfactorily by serving assets over HTTPS.
Update 2022: Chrome 98 is out, and it introduces support for Preflight requests. According to the announcement, failed requests are supposed to produce a warning and have no other effect, but in my case they are full errors that break my development sites. So I had to add middleware to teach webpack-dev-server
how to serve preflight requests.
Private Network Access (formerly CORS-RFC1918) is a specification that forbids requests from less private network resources to more private network resources. Like HTTP to HTTPS, or a remote host to localhost.
The ultimate solution was to add a self-signed certificate and middleware which enabled requests from my remote dev server to my localhost webpack-dev-server
for assets.
Generate certificates
cd path/to/.ssl
npx mkcert create-cert
Configure webpack-dev-server
to use certificates and serve preflight requests
module.exports = {
//...
devServer: {
https: {
key: readFileSync("./.ssl/cert.key"),
cert: readFileSync("./.ssl/cert.crt"),
cacert: readFileSync("./.ssl/ca.crt"),
},
allowedHosts: ".example.dev", // should match host in origin below
setupMiddlewares(middlewares, devServer) {
// Serve OPTIONS requests
devServer.app.options('*', (req, res) => {
// Only serve if request has expected origin header
if (/^https:\/\/example\.dev$/.test(req.headers.origin)) {
res.set({
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Private-Network": "true",
// Using * results in error if request includes credentials
"Access-Control-Allow-Origin": req.headers.origin,
})
res.sendStatus(200)
}
}
return middlewares
}
}
}
Trust certificates
- Right click
ca.crt
in Windows Explorer and select Install Certificate - Select Current User.
- Choose Place all certificates in the following store, then Browse..., and select Trusted Root Certification Authorities.
- Finish.
Firefox-specific instructions
Firefox doesn't respect your authoritah! by default. Configure it to do so with these steps:
- Type
about:config
into the address bar - Search for
security.enterprise_roots.enabled
- Toggle the setting to
true
Solution 2:
just a Chrome client way to ignore this warning and make assets accessable:
1: go to chrome://flags/#block-insecure-private-network-requests
2: set Block insecure private network requests
to Disabled
Note: this just works fine when you're in your own computer or your dev environment