asp.net - application - IIS como proxy inverso: compresión de la respuesta reescrita del servidor backend
url rewrite (4)
El simple hecho de agregar una etiqueta "serverVariables" en la regla de reescritura hizo el truco:
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://otherurl{R:1}" />
<serverVariables>
<set name="HTTP_ACCEPT_ENCODING" value="" />
</serverVariables>
</rule>
</rules>
</rewrite>
Estoy implementando un proxy inverso para enrutar solicitudes a un servidor backend.
Funcionalmente, todo funciona correctamente, sin embargo, me preocupa que todas las respuestas del servidor backend se transfieran al cliente (navegador web) sin compresión.
La configuración es la siguiente:
- Servidor backend, no accesible para público, en un dominio interno. Aloja una aplicación web en
https://internal.app
- Servidor web frontal con IIS 7.5, que aloja el sitio web público principal y actúa como un proxy para el servidor backend. El sitio principal está en
https://site.com
.
Quiero enrutar todas las solicitudes a https://site.com/app/WHATEVER
a https://internal.app/WHATEVER
de forma transparente para los clientes.
Mi configuración actual se basa en las extensiones de IIS de URL Rewrite 2.0 y Application Request Routing IIS. El enfoque general se basa en las directrices de los siguientes artículos:
- Configuración de un proxy inverso utilizando IIS, Reescritura de URL y ARR
- Proxy inverso con URL Rewrite v2 y enrutamiento de solicitud de aplicación
La sección relevante de web.config
de la aplicación site.com
:
<system.webServer>
<rewrite>
<rules>
<rule name="Route the requests for backend app" stopProcessing="true">
<match url="^app/(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="{C:1}://internal.app/{R:1}" />
<serverVariables>
<set name="HTTP_ACCEPT_ENCODING" value="" />
</serverVariables>
</rule>
</rules>
<outboundRules>
<rule name="RewriteBackendAbsoluteUrlsInResponse" preCondition="ResponseIsHtml1">
<match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^http(s)?://internal.app(/:80)?/(.*)" />
<action type="Rewrite" value="/app/{R:3}" />
</rule>
<rule name="RewriteBackendAbsoluteUrlsInRedirects" preCondition="ResponseIsHtml1">
<match serverVariable="RESPONSE_LOCATION" pattern="^http(s)?://internal.app(/:80)?/(.*)" />
<action type="Rewrite" value="/app/{R:3}" />
</rule>
<rule name="RewriteBackendRelativeUrlsInResponse" preCondition="ResponseIsHtml1">
<match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^/(.*)" negate="false" />
<conditions>
<add input="{URL}" pattern="^/app/.*" />
</conditions>
<action type="Rewrite" value="/app/{R:1}" />
</rule>
<rule name="RewriteBackendRelativeUrlsInRedirects" preCondition="ResponseIsHtml1">
<match serverVariable="RESPONSE_LOCATION" pattern="^/(.*)" negate="false" />
<conditions>
<add input="{URL}" pattern="^/app/.*" />
</conditions>
<action type="Rewrite" value="/app/{R:1}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
<urlCompression dynamicCompressionBeforeCache="false" />
</system.webServer>
El problema es que tan pronto como deje de borrar la variable de servidor HTTP_ACCEPT_ENCODING
, cada solicitud que coincida con la regla anterior termina con el siguiente error: HTTP Error 500.52 - URL Rewrite Module Error. Outbound rewrite rules cannot be applied when the content of the HTTP response is encoded ("gzip").
HTTP Error 500.52 - URL Rewrite Module Error. Outbound rewrite rules cannot be applied when the content of the HTTP response is encoded ("gzip").
Soy consciente de este hilo y he seguido esas instrucciones. He configurado dynamicCompressionBeforeCache="false"
como se puede ver arriba, he agregado la entrada de registro necesaria y me he asegurado de que los módulos están en el orden correcto en IIS.
Sin embargo, esto solo parece funcionar solo si la reescritura ocurre dentro de una aplicación web. Si elimino las reglas anteriores y agrego una simple (y las respectivas reglas de salida) para reescribir, por ejemplo, /x/WHATEVER
a solo /WHATEVER
, todo funciona perfectamente sin la necesidad de borrar HTTP_ACCEPT_ENCODING
: la regla funciona y la compresión está habilitada para las solicitudes reescritas .
Pero tan pronto como vuelvo a agregar mi regla que reescribe la respuesta a una aplicación web diferente y no HTTP_ACCEPT_ENCODING
encabezado HTTP_ACCEPT_ENCODING
, aparece el mismo error nuevamente.
Por lo que entiendo, si la reescritura implica otra aplicación web, hay más restricciones en lo que se puede hacer. Por ejemplo, el reescritor de URL debe recibir una respuesta sin comprimir del servidor backend para poder reescribirla usando las reglas de salida. Supongo que borrar HTTP_ACCEPT_ENCODING
en este escenario es una necesidad debido a esto.
Sin embargo, esperaría que dado que el módulo de compresión se encuentra en la parte superior de la lista de módulos, la respuesta final reescrita debería comprimirse sin importar de dónde se originó. Parece que IIS hace algunos accesos directos y devuelve la respuesta al cliente sin pasar por el módulo de compresión. O bien, el encabezado HTTP_ACCEPT_ENCODING
se elimina lo suficientemente pronto como para deshabilitar completamente la compresión (no solo en la comunicación de servidor a servidor).
Entonces, finalmente, mi pregunta es: ¿hay una manera de comprimir esas respuestas?
Lo he descubierto yo mismo.
Qué se necesita hacer para que funcione:
-
Accept-Encoding
encabezadoAccept-Encoding
debe eliminar antes de enrutar la solicitud al servidor back-end, para que la respuesta se pueda reescribir usando reglas de salida - el encabezado debe restaurarse mediante una regla adicional de salida, para que esté presente cuando el módulo de compresión se active antes de que se envíe la respuesta al cliente
He decidido hacerlo así:
agregue una nueva variable de servidor a la regla de reescritura para mantener el encabezado original enviado por el cliente:
<set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
(Lo puse antes de la línea que borra la variable
HTTP_ACCEPT_ENCODING
)agregar una nueva regla de salida:
<rule name="RestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding"> <match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" /> <action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" /> </rule>
y una condición previa que lo acompaña:
<preCondition name="NeedsRestoringAcceptEncoding"> <add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".+" /> </preCondition>
Funciona como un encanto hasta ahora.
PD: la siguiente solución solo funciona si tiene control sobre su servidor de aplicaciones.
Básicamente, es dejar que el servidor web haga la compresión y dejar que el servidor de aplicaciones haga el trabajo pesado de lo que se supone que hace la aplicación (sin compresión).
Si deshabilita la compresión en el servidor de aplicaciones, la respuesta que recibe del servidor de aplicaciones no está comprimida. En el servidor web, debe habilitar la compresión, de modo que el servidor web respete el encabezado HTTP "Aceptar-Codificación: gzip, desinflar" al responder al cliente (navegador).
Esta configuración descargará la CPU en el servidor de aplicaciones, pero aumentará el tráfico de red entre su servidor web y el servidor de aplicaciones. Si está en la red interna, no tiene mucho impacto en el rendimiento.
Para abordar el problema del póster original y al mismo tiempo conservar las respuestas comprimidas con gzip , uno simplemente necesita hacer lo siguiente:
actualice el registro en su servidor web público como tal:
a. Para sitios web de 64 bits, ejecute lo siguiente en una consola de comandos con derechos de administrador:
reg add HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/InetStp/Rewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0
segundo. Para sitios web de 32 bits, ejecute lo siguiente en una consola de comandos con derechos de administrador:
reg add HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432node/Microsoft/Inetstp/Rewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0
restablecer IIS
desactivar la compresión estática
a. para lograr esto,
<urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" />
la siguiente configuración de mi web:<urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" />
en el nodo del servidor en el administrador de IIS, haga doble clic en el ícono de
Modules
, luego a la derecha, haga clic en "ver lista ordenada" y verifique que sus módulos de compresión estática / dinámica estén hacia arriba y el módulo de reescritura de URL esté hacia abajo
Tenga en cuenta
Veo muchas soluciones a este problema flotando en la red donde el encabezado de solicitud HTTP_CONTENT_TYPE se borra como parte de la regla de reescritura de URL (incluidas las respuestas en esta página). Se debe tener en cuenta que, si bien esto resuelve el problema original del error 500.52, la compresión gzip en la respuesta se ELIMINA . Este puede ser el resultado deseado, pero si se requiere la compresión gzip, la solución anterior hará el truco