To get a good security score we are using the response header X-Content-Type-Options "nosniff", however that breaks when using the /ui in production.
The problem seems to be that the cached response from src/ServiceStack/TemplatePagesFeature.cs doesn’t preserve the Content-Type.
To reproduce:
mkdir Example
cd Example
x new empty
ASPNETCORE_ENVIRONMENT=Production dotnet run --project Example --configuration=Release --no-launch-profile
Then in another window run: curl -s --compressed -i https://localhost:5001/ui/Hello | head -n 9 and observe the difference between the first and second response:
The first response correctly has the Content-Type set, and works correctly with the nosniff header. It is however missing the ETag and Cache-Control headers. I think caching should be enabled from the very first request, not just the second.
The second response has the ETag and Cache-Control headers, but is missing the Content-Type.
I would have expected the headers of the two responses to be the same.
Thank you for the swift response.
Disabling compression does work, but is less good for performance.
Unfortunately you fix doesn’t seem to solve the problem.
To reproduce:
mkdir Example
cd Example
x new empty
x mix myget
dotnet restore
dotnet list package
ASPNETCORE_ENVIRONMENT=Production dotnet run --project Example --configuration=Release --no-launch-profile
The dotnet list package returns:
dotnet list package
Project 'Example' has the following package references
[net6.0]:
Top-level Package Requested Resolved
> ServiceStack 6.1.1 6.1.1
When testing the response from another terminal I get:
Example curl -s --compressed -i https://localhost:5001/ui/Hello | head -n 9
HTTP/1.1 200 OK
Content-Length: 47193
Content-Type: text/html; charset=utf-8
Date: Mon, 13 Jun 2022 12:07:45 GMT
Server: Kestrel
Content-Encoding: deflate
<!DOCTYPE html>
<html lang="en" style="">
➜ Example curl -s --compressed -i https://localhost:5001/ui/Hello | head -n 9
HTTP/1.1 200 OK
Content-Length: 47193
Date: Mon, 13 Jun 2022 12:07:47 GMT
Server: Kestrel
Cache-Control: public, max-age=3600, must-revalidate
Content-Encoding: deflate
ETag: "d45dc8f5170510be762b8fcd8cabab90"
<!DOCTYPE html>
So as before, the first response is missing Cache-Control and ETag. You didn’t comment on why they are not present in the first response.
The second response is missing the Content-Type. Seeing your fix adds the httpRes.ContentType I can only assume it is null and therefore has no effect.
I do notice that a problem with nosniff has appeared before, and they could quite possibly have experienced the same problem.
Maybe I should have opened this as a support ticket on GitHub instead?