I was able to get it working. There were two main problems that seem to be unique to IIS: proxy cache and request path characters.
Proxy cache needs to be disabled. I did this by disabling proxy cache and setting the cache timeout to 0 in the Application Request Routing->Proxy Settings module…not sure both are needed; disabling should be enough. With the cache enabled jupyterhub behaved in strange ways. For example, if you stopped a server, and refreshed the hub control panel, it would report the server had not been stopped. If you waited long enough and tried again, it would successfully stop the server.
The second issue was actually the most important as it led to cascading errors. By default, IIS does not allow colons (
in request paths. I use Jupyterlab and it relies on urls like this: api/settings/@jupyterlab/docmanager-extension:plugin
and IIS refused to respond to these requests. I added an entry in the httpRuntime to define disallowed characters, excluding the colon: requestPathInvalidCharacters="<,>,*,%,&,\,?"
(copilot came up with this list so I’m not claiming it’s definitive).
Once those items were in place, the myriad of guides for using IIS as a reverse proxy were on point.
Here’s my web.config for my jupyterhub site.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol allowKeepAlive="true" />
<rewrite>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
<match filterByTags="A, Form, Img" pattern="^http(s)?://localhost:8000/(.*)" />
<action type="Rewrite" value="{R:1}://jupyter.myserver.com/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
<rules>
<rule name="Reverse proxy jupyterhub and rewrite secure-to-non" stopProcessing="true">
<match url="(.)*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{CACHE_URL}" pattern="^(http|ws)(.*)" />
</conditions>
<action type="Rewrite" url="{C:1}://localhost:8000/{R:0}" appendQueryString="true" logRewrittenUrl="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
<system.web>
<customErrors mode="Off" />
<httpRuntime requestPathInvalidCharacters="<,>,*,%,&,\,?" />
</system.web>
</configuration>
I don’t know if there are plans to officially support WSL, but it is worth considering. GPU Paravirtualization is baked into WSL, making it very easy to make a GPU available to the hubs.