I trying to configure httpCompression on IIS7. By googling, I found that it can be made using httpCompression section in config. The problem, that I can't make it work from web.config.
When I make the configuration in applicationHost.config everything works as needed, but I want to be able to make this configuration per application and not globally.
I changed section definition in applicationHost.config to <section name="httpCompression" overrideModeDefault="Allow" /> and moved httpCompression section to web.config:
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/json" enabled="true" />
<add mimeType="application/json; charset=utf-8" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
</httpCompression>
What am I missing? It looks like IIS not reads compression configurations from web.config at all.
After each change, I make application pool recycle, so it not a problem.
as per this ServerFault answer: https://serverfault.com/a/125156/117212 - you can't change httpCompression in web.config, it needs to be done in applicationHost.config file. Here is the code I use in my Azure web role to modify applicationHost.config file and add mime types for compression:
using (var serverManager = new ServerManager())
{
var config = serverManager.GetApplicationHostConfiguration();
var httpCompressionSection = config.GetSection("system.webServer/httpCompression");
var dynamicTypesCollection = httpCompressionSection.GetCollection("dynamicTypes");
Action<string> fnCheckAndAddIfMissing = mimeType =>
{
if (dynamicTypesCollection.Any(x =>
{
var v = x.GetAttributeValue("mimeType");
if (v != null && v.ToString() == mimeType)
{
return true;
}
return false;
}) == false)
{
ConfigurationElement addElement = dynamicTypesCollection.CreateElement("add");
addElement["mimeType"] = mimeType;
addElement["enabled"] = true;
dynamicTypesCollection.AddAt(0, addElement);
}
};
fnCheckAndAddIfMissing("application/json");
fnCheckAndAddIfMissing("application/json; charset=utf-8");
serverManager.CommitChanges();
}
ServerManager comes from Microsoft.Web.Administration package in NuGet.
You should check the whole config file hierarchy.
If you removed the section from applicationHost you may be inheriting from machine.config or a web.config of a parent directory.
Related
I'm working on an ASP.NET webforms and I put this code in my web.config :
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll"/>
<dynamicTypes>
<add mimeType="text/*" enabled="true"/>
<add mimeType="message/*" enabled="true"/>
<add mimeType="application/javascript" enabled="true"/>
<add mimeType="application/x-javascript" enabled="true"/>
<add mimeType="*/*" enabled="false"/>
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true"/>
<add mimeType="message/*" enabled="true"/>
<add mimeType="application/javascript" enabled="true"/>
<add mimeType="application/x-javascript" enabled="true"/>
<add mimeType="*/*" enabled="false"/>
</staticTypes>
</httpCompression>
<urlCompression doStaticCompression="true" doDynamicCompression="true"/>
Sometimes Google Page Speed Insights and PageSpeed extension on Chrome don't say the same message about enable compression. Sometimes there are about 10 files so it's high importance, sometimes it's just 4 files, so medium importance... Sometimes Mobile version shows 10 files while computer version shows 4. It seems to be random. For instance. I analyse at 11:00 it's not the same at 11:30 and not the same at 13:00... Can change everytime without data or code changes.
Can someone explain this strange thing ?
By the way, I don't know why among the 4 remaining files, there are still a css file and a js file not "enabled" since other css and js files are not in the list anymore.
I also would like to remove the WebResource.axd?d=...&t=... from the 4 remaining files on some pages. That's why I added a mimeType x-javascript. But it doesn't seem to work.
Thanks.
here what you should do:
1. Enable dynamic compression in IIS7 enable compression
2. Enable page compression by using this code:
public static bool IsGZipSupported()
{
string AcceptEncoding = HttpContext.Current.Request.Headers[«Accept-Encoding»];
if (!string.IsNullOrEmpty(AcceptEncoding) &&
(AcceptEncoding.Contains(«gzip») || AcceptEncoding.Contains(«deflate»)))
return true;
return false;
}
public static void GZipEncodePage()
{
if (IsGZipSupported())
{
HttpResponse Response = HttpContext.Current.Response;
string AcceptEncoding = HttpContext.Current.Request.Headers[«Accept-Encoding»];
if (AcceptEncoding.Contains(«gzip»))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader(«Content-Encoding», «gzip»);
}
else
{
Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader(«Content-Encoding», «deflate»);
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
HtmlUtil.GZipEncodePage();
}
Enable static content enable static content
In previous versions all of these settings could be added and tweaked in the Web.Config file using something like the code below:
<staticContent>
<mimeMap fileExtension=".webp" mimeType="image/webp" />
<!-- Caching -->
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="96:00:00" />
</staticContent>
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
<urlCompression doStaticCompression="true" doDynamicCompression="true"/>
However, with the Web.Config no longer being around in ASP.NET vNext, how do you adjust settings like this? I have searched the net and the ASP.NET Github repo, but not come across anything - any ideas?
As "agua from mars" states in the comments, if you're using IIS you can use IIS's static file handling, in which case you can use the <system.webServer> section in a web.config file and that will work as it always did.
If you're using ASP.NET 5's StaticFileMiddleware then it has its own MIME mappings that come as part of the FileExtensionContentTypeProvider implementation. The StaticFileMiddleware has a StaticFileOptions that you can use to configure it when you initialize it in Startup.cs. In that options class you can set the content type provider. You can instantiate the default content type provider and then just tweak the mappings dictionary, or you can write an entire mapping from scratch (not recommended).
ASP.NET Core - mime mappings:
If the extended set of file types you are providing for the entire site are not going to change, you can configure a single instance of the ContentTypeProvider class, and then leverage DI to use it when serving static files, like so:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddInstance<IContentTypeProvider>(
new FileExtensionConentTypeProvider(
new Dictionary<string, string>(
// Start with the base mappings
new FileExtensionContentTypeProvider().Mappings,
// Extend the base dictionary with your custom mappings
StringComparer.OrdinalIgnoreCase) {
{ ".nmf", "application/octet-stream" }
{ ".pexe", "application/x-pnal" },
{ ".mem", "application/octet-stream" },
{ ".res", "application/octet-stream" }
}
)
);
...
}
public void Configure(
IApplicationBuilder app,
IContentTypeProvider contentTypeProvider)
{
...
app.UseStaticFiles(new StaticFileOptions() {
ContentTypeProvider = contentTypeProvider
...
});
...
}
90% of the pages on my website follows this syntax
http://www.thisismysite.com/ShowProduct.aspx?ID=29
http://www.thisismysite.com/BrowseProducts.aspx?CatID=58
I was checking Google webmaster tools and saw that my site is generating errors for the following url's
http://www.thisismysite.com/ShowProduct.aspx%20ID=50
http://www.thisismysite.com/BrowseProducts.aspx%20CatID=58
http://www.thisismysite.com/ShowProduct.aspx%3FID%3D900
http://www.thisismysite.com/ShowProduct.aspx%3FID=727
http://www.thisismysite.com/ShowProduct.aspx%3FID=64
http://www.thisismysite.com/GetProductsRss.aspx%3FCatID%3D60
When I browsed these url's, I got the error
Server Error in '/' Application.
Runtime Error
Description: An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.
How can I fix these errors? I know about the relaxedUrlToFileSystemMapping="true" but isn't that a hack? What's the proper way to handle these url's
A portion of my web.config looks like this
<system.web>
<httpRuntime requestValidationMode="2.0" />
<authentication mode="Forms">
<forms cookieless="UseCookies" loginUrl="~/AccessDenied.aspx" name="TBFORMAUTH" />
</authentication>
<pages theme="TemplateM" masterPageFile="~/Template.master" maintainScrollPositionOnPostBack="false" validateRequest="false" enableEventValidation="false" viewStateEncryptionMode="Never" controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">
</pages>
<!--
Possible modes are "transitional", "strict", and "legacy".
<xhtmlConformance mode="transitional" />
-->
<compilation debug="false" targetFramework="4.0">
<assemblies>
<add assembly="System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C53R34E089" /></assemblies></compilation>
<sessionState mode="InProc" cookieless="false" timeout="15" />
<roleManager enabled="true" cacheRolesInCookie="true" cookieName="TBROLES" defaultProvider="TB_RoleProvider">
<providers>
<add connectionStringName="LocalSqlServer" applicationName="/" name="TB_RoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f22f50a3a" />
</providers>
</roleManager>
<anonymousIdentification cookieless="UseCookies" enabled="false" />
<profile defaultProvider="TB_ProfileProvider">
<providers>
<add name="TB_ProfileProvider" connectionStringName="LocalSqlServer" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f64f1d50a3a" />
</providers>
<properties>
<add name="FirstName" type="String" />
<add name="LastName" type="String" />
<add name="Gender" type="String" />
<add name="BirthDate" type="DateTime" />
<add name="Occupation" type="String" />
<add name="Website" type="String" />
<group name="Forum">
<add name="Posts" type="Int32" />
<add name="AvatarUrl" type="String" />
<add name="Signature" type="String" />
</group>
<group name="Address">
<add name="Street" type="String" />
<add name="PostalCode" type="String" />
<add name="City" type="String" />
<add name="State" type="String" />
<add name="Country" type="String" />
</group>
<group name="Contacts">
<add name="Phone" type="String" />
<add name="Fax" type="String" />
</group>
<group name="Preferences">
<add name="Theme" type="String" allowAnonymous="false" />
<add name="Culture" type="String" defaultValue="en-US" />
</group>
</properties>
</profile>
<webParts enableExport="true">
<personalization defaultProvider="TB_PersonalizationProvider">
<providers>
<add name="TB_PersonalizationProvider" connectionStringName="LocalSqlServer" type="System.Web.UI.WebControls.WebParts.SqlPersonalizationProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
<authorization>
<allow roles="Administrators,Editors" verbs="enterSharedScope" />
</authorization>
</personalization>
</webParts>
<machineKey validationKey="287C5D125D6B7E7223E1F719E3D58D17BB9677030175D6B7E7223E1F719E3D58D17BBC7E59800B5D4C2EDD5B5D6B7E7223E1F719E3D58D17BBBAF260D9D374A74C76CB741803" decryptionKey="5C1D8BD9DF3E1B4E1D05C1D8BD9DF616E0D5C1D8BD9DF" validation="SHA1" />
<customErrors defaultRedirect="~/Error.aspx" redirectMode="ResponseRewrite">
</customErrors>
<urlMappings>
<add url="~/articles/beer.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=28" />
<add url="~/articles/events.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=41" />
<add url="~/articles/news.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=31" />
<add url="~/articles/photos.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=40" />
<add url="~/articles/blog.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=29" />
<add url="~/articles/faq.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=42" />
</urlMappings>
<healthMonitoring heartbeatInterval="10800">
<providers>
<remove name="SqlWebEventProvider" />
<add name="SqlWebEventProvider" connectionStringName="LocalSqlServer" buffer="false" bufferMode="Notification" maxEventDetailsLength="1073741823" type="System.Web.Management.SqlWebEventProvider,System.Web,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7fse350a3a" />
</providers>
<eventMappings>
<add name="TB Events" type="MB.Customs.WebCustomEvent, MB.Customs.CustomEvents" />
</eventMappings>
<rules>
<clear />
<add name="TB Events" eventName="TB Events" provider="SqlWebEventProvider" profile="Critical" />
<add name="All Errors" eventName="All Errors" provider="SqlWebEventProvider" profile="Critical" />
<add name="Failure Audits" eventName="Failure Audits" provider="SqlWebEventProvider" profile="Critical" />
<add name="Heartbeats" eventName="Heartbeats" provider="SqlWebEventProvider" profile="Critical" />
</rules>
</healthMonitoring>
</system.web>
Because these external links are URL encoded, ASP.NET will try to find files or routes with that name. You could try messing around with the routing framework to be smart about looking for URLs that start with the routes in your application. However, this would probably mean each page needs additional logic within them to parse the improperly encoded requests. Another solution is to add custom error handling for the 404 that ASP.NET will generate when you know how to best redirect the user.
Probably the best way you could handle this error would be to intercept errors in your Applications Global.asax.cs file. You could try something like the following in your Application_Error method:
protected void Application_Error(Object sender, EventArgs e)
{
bool httpError = Context.Error is HttpException;
if (httpError && ((HttpException)Context.Error).GetHttpCode() == 404)
{
// Convert the path to lowercase. This should ONLY be used to make finding indices easier, but NOT used when generating the redirect path since case could be important.
string absolutePath = Request.Url.AbsolutePath.ToLowerInvariant();
int extensionLength = ".aspx".Length;
int questionMarkIdx = absolutePath.IndexOf("?");
int encodedQuestionMarkIdx = absolutePath.IndexOf(".aspx%3F") + extensionLength;
int encodedSpaceIdx = absolutePath.IndexOf(".aspx%20") + extensionLength;
// Handle encoded question mark
if ((questionMarkIdx == -1) && (encodedQuestionMarkIdx > extensionLength))
{
string correctPath = Request.Url.AbsolutePath.Substring(0, absolutePath.Length - encodedQuestionMarkIdx);
// Add 3 here to exclude the "%3F" in the result
string encodedQueryString = Request.Url.AbsolutePath.Substring(encodedQuestionMarkIdx + 3);
Response.Redirect(correctPath + "?" + HttpUtility.UrlDecode(encodedQueryString));
}
// Handle encoded space
if ((questionMarkIdx == -1) && (encodedSpaceIdx > extensionLength))
{
string correctPath = Request.Url.AbsolutePath.Substring(0, absolutePath.Length - encodedSpaceIdx);
// Add 3 here to exclude the "%20" in the result
string encodedQueryString = Request.Url.AbsolutePath.Substring(encodedSpaceIdx + 3);
Response.Redirect(correctPath + "?" + HttpUtility.UrlDecode(encodedQueryString));
}
}
}
I wrote this answer on a non-Windows machine, so I don't have a way to test it but this should be enough to get you in the general direction you should go in solving this problem.
The important point is to make a distinction between 404s that happened because the URL was improperly encoded and 404s that happened because of some other generic reason you're unable to handle. You only want to handle 404s due to URL encoding issues. All other errors and other 404 messages should be propagated back to your default handlers.
Also an important thing to check is what the referrer is on these requests to bad URLs. You should look in Google Webmaster tools to see if they tell you. If they don't you need to check your logs (or ensure you make some!) to find out where they're coming from. Once you know the source you should contact the site owner to let them know they're serving out bad URLs. Trying to compensate for broken external links is not foolproof and is a very difficult challenge that can become a full-time job on its own, so you're best off trying to stop them from being generated in the first place if it's within your power to make it happen.
I am trying to remove one directory from being gziped as it causes errors on the browser.
Here is the code I have. I've read that I can't disable one page only, but there is a way to disable a directory path. I don't have access to server settings, but maybe I could do it from web.config settins by removing or adding a path to staticTypes?
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" staticCompressionLevel="9" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/json" enabled="true" />
<add mimeType="*/*" enabled="false" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
Please advice
I'm having difficulty making IIS 7 correctly compress a Json result from ASP.NET MVC. I've enabled static and dynamic compression in IIS. I can verify with Fiddler that normal text/html and similar records are compressed. Viewing the request, the accept-encoding gzip header is present. The response has the mimetype "application/json", but is not compressed.
I've identified that the issue appears to relate to the MimeType. When I include mimeType="*/*", I can see that the response is correctly gzipped. How can I get IIS to compress WITHOUT using a wildcard mimeType? I assume that this issue has something to do with the way that ASP.NET MVC generates content type headers.
The CPU usage is well below the dynamic throttling threshold. When I examine the trace logs from IIS, I can see that it fails to compress due to not finding a matching mime type.
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files" noCompressionForProxies="false">
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/json" enabled="true" />
</dynamicTypes>
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="application/json" enabled="true" />
</staticTypes>
</httpCompression>
Make sure your %WinDir%\System32\inetsrv\config\applicationHost.config contains these:
<system.webServer>
<urlCompression doDynamicCompression="true" />
<httpCompression>
<dynamicTypes>
<add mimeType="application/json" enabled="true" />
<add mimeType="application/json; charset=utf-8" enabled="true" />
</dynamicTypes>
</httpCompression>
</system.webServer>
From the link of #AtanasKorchev.
As #simon_weaver said in the comments, you might be editing the wrong file with a 32 bit editor on a 64 bit Windows, use notepad.exe to make sure this file is indeed modified.
I have successfully used the approach highlighted here.
Use this guide
None of these answers worked for me. I did take note of the application/json; charset=utf-8 mime-type though.
I recommend this approach
Create CompressAttribute class, and set target action.
The ActionFilterAttribute approach updated for ASP.NET 4.x and Includes Brotli.NET package.
using System;
using System.IO.Compression;
using Brotli;
using System.Web;
using System.Web.Mvc;
public class CompressFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpRequestBase request = filterContext.HttpContext.Request;
string acceptEncoding = request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(acceptEncoding)) return;
acceptEncoding = acceptEncoding.ToUpperInvariant();
HttpResponseBase response = filterContext.HttpContext.Response;
if (acceptEncoding.Contains("BR"))
{
response.AppendHeader("Content-encoding", "br");
response.Filter = new BrotliStream(response.Filter, CompressionMode.Compress);
}
else if (acceptEncoding.Contains("GZIP"))
{
response.AppendHeader("Content-encoding", "gzip");
response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
}
else if (acceptEncoding.Contains("DEFLATE"))
{
response.AppendHeader("Content-encoding", "deflate");
response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
}
}
}