Jupyterhub on WSL2 with IIS reverse proxy--no websockets

I’ve got jupyterhub running in WSL2 (Server 2025) and everything works wonderfully on localhost:8000. I can even netsh the WSL ip and to get a public connection at mydomain.com:8000. I cannot get the reverse proxy on IIS working correctly. I can get a proxy setup to jupyter.mydomain.com and the hub will load but any functionality that depends on websockets will fail (i.e., no kernel, no terminal). I’ve tried different ways of setting proxy headers for ‘Upgrade’ and ‘Connection’ to match what is demonstrated in the nginx/apache2 websocket configs but I haven’t had any luck.

Does anyone have any insight on how to properly configure IIS to serve as a reverse proxy for jupyterhub?

The only errors raised are JS errors indicating that a websocket connection failed.

I’m not familiar with IIS, but I found this doc on enabling websockets

If that doesn’t work, and no-one else here has advice, you could try asking on a Microsoft/IIS forum?

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 (:slight_smile: 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="&lt;,>,*,%,&amp;,\,?" />
    </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.