Problem: My HTML/JavaScript app receives a CORS error processing a redirect.
Objective: Configure Apache to include an HTTP header only during a specific redirect.
Important note: This HTML runs in a browser from a locally loaded file and not from a page served by a web server.
The Code:
<body>
<div id="response">Loading page ...</div>
<script type="text/javascript">
async function get_response() {
let url = 'https://example.com/endpoint'
fetch(url, {
redirect: "follow"
})
.then(response => response.text())
.then(data => {
document.getElementById('response').innerHTML = data;
})
.catch(error => {
console.log(error);
});
}
get_response();
</script>
</body>
Code Description:
The above code fetches data from a URL and displays it in the browser. However, the website sends a 302 redirect (by design). The redirect causes a CORS error.
CORS Error:
Access to fetch at 'https://example.com/endpoint' from origin 'null' 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.
The Apache configuration that generates the redirect:
RewriteEngine on
RewriteRule ^(/endpoint)$ /system/endpoint.php? [END,NE,R=302]
Possible workarounds that are not acceptable:
Configure Apache to send a CORS header before the RewriteRule:
Header always set Access-Control-Allow-Origin "*"
This is not a good solution because I do not want to send this header for all URLs only this one redirect. Other pages do set this header and when Chrome receives several Access-Control-Allow-Origin headers with the same value, an error is generated instead of accepting the headers. Note: AFAIK header always set is required to include the header for 3xx responses.
- Modify the application to use the redirected URL instead of a URL that redirects.
This will not work as the final URL is configurable and the objective is to not modify the application when the backend routes change.
Other Workarounds:
I realized by writing this question and thinking through the problem, that my RewriteRule is generating a redirect that is not necessary for all situations. For a redirect within the same domain, a URL rewrite is enough. For redirecting to subdomains, which I need to do, rewriting the URL is not enough.
For the same domain URL change, this will work:
RewriteRule ^(/endpoint)$ /system/endpoint.php? [L]
For a true redirect, I need a method to conditionally include the CORS header.
CodePudding user response:
The solution is to combine the Apache <If>
directive with the server variable REQUEST_URI
:
<If "%{REQUEST_URI} == '/endpoint'">
Header always set Access-Control-Allow-Origin "*"
</If>
RewriteEngine on
RewriteRule ^(/endpoint)$ https://example2.com/system/endpoint? [END,NE,R=302]
The new endpoint must also be configured to respond with the Access-Control-Allow-Origin
header.
[Update]
By responding with Access-Control-Allow-Origin "*"
, my solution defeats the purpose of CORS which is to prevent unauthorized resource sharing. This solution will trigger a warning or a violation in a security audit. Since I am only wildcarding specific routes in the backend and not all routes, this can be explained/documented.
When I researched this issue on Stack Overflow and on the Internet, each answer/article recommended returning the `Access-Control-Allow-Origin "*" header. That is the wrong solution almost of the time. The purpose of a security control is to enforce it, and not to bypass it.