Web Farms and ASP.NET ViewState

If you deploy ASP.NET websites to a web farm, you may run into this perplexing System.Web.HttpException:

The viewstate is invalid for this page and might be corrupted

If you've installed ASP.NET 1.1 service pack 1, you may also get a much more helpful exception from System.Web.UI.LosFormatter.Deserialize:

Authentication of viewstate failed. 1) If this is a cluster, edit configuration so all servers use the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster. 2) Viewstate can only be posted back to the same page. 3) The viewstate for this page might be corrupted.

So clearly there's a problem with the ASP.NET viewstate.

As pointed out in Rich Crane's blog entry, ASP.NET ViewState is tied to the particular server it came from by default-- even though the documentation says it isn't. So when ViewState generated on server A is POST-ed back to server B, you'll get this exception. Somewhere in the pipeline, the viewstate is salted with a unique, autogenerated machine key from the originating server's machine.config file:

<!--  validation="[SHA1|MD5|3DES]" -->
<machineKey validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps" validation="SHA1"/>

This is done to prevent users from somehow tampering with the ViewState. Any change to the ViewState data on the client will be detected. But this has a side effect: it also prevents multiple servers from processing the same ViewState. One solution is to force every server in your farm to use the same key-- generate a hex encoded 64-bit or 128-bit <machineKey> and put that in each server's machine.config (note that this key is bogus and shown only for illustration; don't use it):

<!--  validation="[SHA1|MD5|3DES]" -->
<machineKey validation="SHA1"
validationKey="F3690E7A3143C185A6A8B4D81FD55DD7A69EEAA3B32A6AE813ECEEC" />

Or-- and I think this is the easier approach-- you can disable the keying of viewstate to a particular server using a simple page directive at the top of your .aspx pages:

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="MyPage.aspx.vb"
Inherits="MyAssembly.MyPage" enableViewStateMac="False" %>

Alternately, you can modify the pages element in Web.config:

<system.web>
<pages enableViewStateMac="false" />
</system.web>

Either way, works great. Who needs all that stupid security anyway?