Reputation: 81386
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.
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.
Upvotes: 1
Views: 1324
Reputation: 81386
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.
Upvotes: 1