Prevent Google Chrome from sending Sec-Fetch headers - caching

I would like to cache static content (index.html) in my web API 2 (net. framework 4.6.2 app)
I wrote OWIN middleware that adds a cache-control header to the response, allowing for subsequent requests to be retrieved from the browser cache.
The OWIN context extension:
public static class BrowserCacheOwinContextExtensions {
public static IOwinContext SetCacheControlHeader(this IOwinContext context, int maxAge) {
context.Response.Headers["Cache-Control"] = $"public, max-age={maxAge}";
context.Response.Headers["Vary"] = "User-Agent";
return context;
}
}
Snippet from middleware:
if (browserCacheOptions.IsEnable) {
context.SetCacheControlHeader(browserCacheOptions.MaxAge);
}
await context.Response.WriteAsync(file);
It works fine in the Mozilla browser but doesn't in Chrome.
Snippet from Mozilla:
I believe the root cause of this is that Chrome adds additional Sec-Fetch headers + cache-control: max-age=0 to the request automatically.
Automatically added Sec-Fetch headers by Chrome:
Note: if open the same request at separate browser tab it works fine ever for Chrome (no sec-fetch headers in request)
Q: Is it possible to somehow disable such behaviour and don't add sec-fetch headers automatically to the request?
Or if you have other proposals, please do share them.

You cannot modify the sec- as it is in forbidden headers list.
This basically a list of headers that cannot be modified using the client side scripting.

Related

Why isn't redirection based localization working with Blazor Server on Firefox?

We are using dynamically set culture based on cookies and redirection with Blazor Server, with 100% fidelity on the set-up in the documentation with a CultureController:
public IActionResult Set(string culture, string redirectUri)
{
if (culture != null)
{
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(
new RequestCulture(culture, culture)));
}
return LocalRedirect(redirectUri);
}
With Chromium based browsers and Safari there are no problems: the Set-Cookie header on the response is working great and then the browser is redirected.
With Firefox though, it seems the request to the controller is being aborted for some reason, thus not receiving the Set-Cookie header before the redirection and ultimately not changing the culture:
Here is the same thing on Chrome where the header is working:
I have checked for Cookie attributes (tried everything: Strict, Secure, etc. No luck), if there were any options to add for the LocalRedirect to wait and not cancel other requests.
Any leads would be greatly appreciated.
Edit: Additionnal info (StackTrace of the blocked request)
Seems like the request gets canceled by Blazor SignalR connection reset, which seems to have a timing issue only on Firefox from what I understand (or Firefox treats it differently somehow).

ASP.NET MVC Page with ResponseCache on action DOES return new content instead of cache

I followed this tutorial (https://learn.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-2.1) to implement ResponseCache on my controller-action.
In short, I added
services.AddResponseCaching(); and app.UseResponseCaching(); in the startup and this tag [ResponseCache( Duration = 30)] on my controller.
Then I added a <h2>#DateTime.Now</h2> in my view and what I expected.... was the same datetime.now for 30 seconds.
But it doesn't, it just shows the new time on every reload (F5).
I made sure my devtools in chrome do not say 'disable cache'.
It's both with and without the chrome devtools open, on my local machine, now trying on a brandnew .net core mvc project.
One thing I noticed (with devtools open) is that the request has this header: Cache-Control: max-age=0. Does this influence the behaviour?
I thought it would mean something because it looks like the request says 'no cache' but that strikes me as weird because I didn't put the header in and I would say the default behaviour of chrome wouldn't be to ignore caches?
A header like Cache-Control: max-age=0 effectively disables all caching. Resources are basically expired as soon as they come off the wire, so they are always fetched. This header originates from the server. The client has nothing to do with it.
Assuming you haven't disabled response caching manually in some way by accident. Then, the most likeliest situation is that you're doing something where the response caching middleware will never cache. The documentation lists the following conditions that must be satisfied before responses will be cached, regardless of what you do:
The request must result in a server response with a 200 (OK) status code.
The request method must be GET or HEAD.
Terminal middleware, such as Static File Middleware, must not process the response prior to the Response Caching Middleware.
The Authorization header must not be present.
Cache-Control header parameters must be valid, and the response must be marked public and not marked private.
The Pragma: no-cache header must not be present if the Cache-Control header isn't present, as the Cache-Control header overrides the Pragma header when present.
The Set-Cookie header must not be present.
Vary header parameters must be valid and not equal to *.
The Content-Length header value (if set) must match the size of the response body.
The IHttpSendFileFeature isn't used.
The response must not be stale as specified by the Expires header and the max-age and s-maxage cache directives.
Response buffering must be successful, and the size of the response must be smaller than the configured or default SizeLimit.
The response must be cacheable according to the RFC 7234 specifications. For example, the no-store directive must not exist in request or response header fields. See Section 3: Storing Responses in Caches of RFC 7234 for details.
However, in such situations, the server should be sending Cache-Control: no-cache, not max-age=0. As a result, I'm leaning towards some misconfiguration somewhere, where you have set this max age value and either forgot or overlooked it.
This is working for me in a 3.1 app to not let F5/Ctrl+F5 or Developer Tools in Firefox or Chrome bypass server cache for a full response.
In startup add this little middleware before UseResponseCaching().
// Middleware that fixes server caching on F5/Reload
app.Use(async (context, next) =>
{
const string cc = "Cache-Control";
if (context.Request.Headers.ContainsKey(cc))
{
context.Request.Headers.Remove(cc);
}
const string pragma = "Pragma";
if (context.Request.Headers.ContainsKey(pragma))
{
context.Request.Headers.Remove(pragma);
}
await next();
});
app.UseResponseCaching();
Haven't noticed any problems...

why spring boot set Expires header to Expires:?

I'm trying to cache control all static css/js files. the codes look like:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/console/*.js","/console/*.css","/console/*.png","/console/*.svg")
.addResourceLocations(
ResourceUtils.CLASSPATH_URL_PREFIX + "/public/console/"
).setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic());
}
I can see the Cache-Control header is correctly set. But why the Expires is always blank ? I'm expecting refresh for all css/js should return 304 Not Modified rather than 200 OK?
Any idea why ? Thanks
The Expires is not needed anymore and recent versions of Spring adopted best practices for front-end caching (see CacheControl and the original commit).
Now if you're not getting the expected result, it might be because your browser is sending a Cache-Control: no-cache request header. This usually happens if you've checked a "disable cache" checkbox in your browser developer tools or if you've refreshed the page with "ctrl+R".
If this is related to Spring Security, you might want to subscribe to this issue and use the following workaround
If not, please provide more information in your question (request and response headers are a good start).

SignalR responses overwriting headers

I've built a simple SignalR hub that lives within a WebAPI service, I've included all the required CORS attributes on both WebAPI and SignalR. My WebAPI endpoints are all working as expected but SignalR isn't.
I've tried all I can think of and all I can find online but nothing works, I already tried this answer, and this other to no solution.
My SignalR extension method looks like this
public static IAppBuilder UseSignalrNotificationService(this IAppBuilder app)
{
var config = new HubConfiguration();
config.Resolver = new HubDependencyResolver();
config.EnableDetailedErrors = true;
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR(config);
return app;
}
And I even tried adding the response headers on all requests using the Web.config but I allways get the same error:
XMLHttpRequest cannot load https://MyApplicationServer/notifications/signalr/negotiate?clientProtocol=1.5&access_token=&connectionData=. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'MyOriginService' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.
After more research and fiddling with the server side of the problem, I ran into this answer and found the error to be with the client side of the request. according to this GitHub issue, the "withCredentials" parameter of the request is always set to 'true'. The solution was to call on the client the start method as follows:
$.connection.hub.start({ withCredentials: false }).done(function () { //... }
Are you changing the request somewhere with some kind of global interceptor? For some reason, the XMLHttpRequest starts with withCredentials:true and this is forbidden when the Access-Control-Allow-Origin is set to *.
What about setting the 'Access-Control-Allow-Origin' to 'http://MyApplicationServer'? It's safer than * and will remove your problem at source.

How can I set "Content-Length" for Visual Studio web performance test request?

I have created a web performance test in VS 2015, and am trying to set request headers. When setting headers such as "Referer" or "User-Agent" everything seems to work fine. I just set the header by right-clicking the request and adding a new header, and when I run the test and inspect the request I can see that the new header has been added. However if I try to add a new header named "Content-Length" and then run the test, this header is missing from the request. I'm getting an http error which I assume is because VS doesn't use the "Content-Length" header:
HTTP Error 411. The request must be chunked or have a content length.
I ran into this problem as well. The consistent trend with these issues online seems to be when the content-length is 0. Otherwise VS seems to dynamically come up with the content-length header on it's own.
Explanation of Problem
Esentially, I don't think you can add it manually. VS wants to add this itself, so the real problem is why isn't VS adding this?
The problem comes from the request being simply a "Request" and not a "Web Service Request", the significant difference here being that the latter includes the "String Body" component. I'm assuming that VS does this because the content-length it recorded was 0. I recorded my workflow in Fiddler and got the same result. I would also assume that the reason adding the Content-Length header doesn't work is because this gets calculated by Visual Studio if the request has a body in the request.
The Workaround
Make the request a "Web Service Request" so that it includes the String Body component. In the test, right click and choose "Insert Web Service Request".
I got clued into this from the below post (which also includes an example for a coded UI test):
https://social.msdn.microsoft.com/Forums/en-US/da492346-760e-4971-a666-8897ae7b35e3/length-required-error?forum=vstswebtest
Plugin Option
I couldn't find a way to just convert my existing request into a Web Service Request, so I created a WebTestRequestPlugin that can be added to a request to dynamically add a body to the request.
Add the below to your project
Change the namespace to match your project's name
Build your solution.
Right click on the problematic request and click "Add Request Plugin" and then select this plugin.
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.ComponentModel;
namespace YourProjectName
{
[DisplayName("Add Request Body")]
[Description("If a request is intended to have a content-length of 0, the Web Test won't include an empty String Body and the Content-Length header won't " +
"be added during test execution. This may result in a 411 - Length required error. This plugin can be added to that request to add a body to the request" +
"during test execution.")]
public class AddBodyToRequestPlugin : WebTestRequestPlugin
{
public AddBodyToRequestPlugin()
{
}
public override void PreRequest(object sender, PreRequestEventArgs e)
{
// Add string body if it doesn't exist yet.
if ( e.Request.Body == null )
{
e.Request.Body = new StringHttpBody();
}
}
}
}

Resources