angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Flask's SERVER_NAME, subdominios y 404 errores

La configuración de Flask's SERVER_NAME puede dar 404 errores si está usando subdominios.

25 noviembre 2020
En Flask
post main image
https://unsplash.com/@azhar93

Este es un corto post sobre Flask y la config variable SERVER_NAME. Como muchos desarrolladores me encontré con esto en un momento determinado, y pensé que compartía mi historia. Con suerte esto evitará dolores de cabeza para algunos.

Mis sitios web deben estar disponibles escribiendo las siguientes direcciones en el navegador:

  • https://example.com = 'without-www', y,
  • https://www.example.com = 'with-www'

Además, para este sitio web había decidido que los URL de las páginas generadas debían ser "without-www" o "with-www", dependiendo de cuál fuera el URL de la solicitud. Esto significa que si escribo:

https://example.com/contact

no hay redireccionamiento y todos los enlaces en la página generada (aparecerán) comenzarán con https://example.com. O, si escribo:

https://www.example.com/contact

no hay redireccionamiento y todos los enlaces en la página generada (aparecerán) comenzarán con https://www.example.com.

Debo añadir que esta no es la mejor práctica y que siempre debe utilizar un solo canonical name, lo que significa que su página web es o bien "with-www" o "without-www", ¡y nunca ambos! En un próximo post entraré en detalles sobre esto.

De todos modos, lo anterior funcionó bien para todos mis sitios web excepto uno, este sitio web de hecho. Hace meses esto no era un problema pero cambió de repente después de una importante actualización de código. Para prevenir los errores 404 de la versión 'with-www' lo solucioné rápidamente haciendo que Nginx redirigiera 'with-www' a 'without-www'. Y luego hubo otros proyectos más urgentes y pasaron meses.

Recientemente estuve actualizando mi servidor (ISPConfig) y pensé en intentarlo de nuevo. Por supuesto, todavía hay 404 errores en la versión "with-www".

Hice algo de tiempo y decidí crear la misma situación en mi PC de desarrollo. Esto significaba copiar los certificados Letsencrypt a mi PC y modificar ligeramente la configuración de desarrollo cambiando FLASK_ENV a 'production' y eliminando el 'with-www' a 'without-www' redirigir en Nginx. Dejé FLASK_DEBUG=True para poder poner puntos de ruptura (excepciones) en el código Python y ver qué estaba pasando.

Afortunadamente, experimenté los mismos errores 404 al cambiar de 'without-www' a 'with-www' en mi PC. Luego, después de algún tiempo reinicié Flask y ahora de repente la versión 'with-www' funcionó y la versión 'Q 4_959_TNEMECALPER_4Q' dio 404 errores. ¿Qué? ¿Al revés?

Buscando en internet por 'Flask ' reboté en páginas, ver los enlaces de abajo, que insinuaban que la versión config variable SERVER_NAME podría ser el problema.

Entonces recordé que añadí SERVER_NAME a mi config cuando estaba trabajando en las tareas de Celery . Hice esto porque pensé que podría ser útil para generar plantillas de Jinja con enlaces url_for() en algunas tareas. Más tarde descarté esta idea. Las tareas deberían ser sencillas y no contener campanas y silbidos. Pero no eliminé el código SERVER_NAME , porque todo iba bien, y después de establecer la redirección en Nginx había otros proyectos más urgentes, qué más hay de nuevo.

Descripción de lo que pasó

Mi factory.py (__init__.py) parecía:

def  create_app():
    ...
    @app.before_request
    def  before_request():
        ....
        # get server_name from  http_host
        if current_app.config.get('SERVER_NAME') is  None:
             http_host = request.environ.get('HTTP_HOST')
            current_app.config['SERVER_NAME'] =  http_host
        ....

Aquí obtengo SERVER_NAME de la solicitud si no se ha establecido. Esto significa que una vez que config[SERVER_NAME] se establece, ya no se actualiza. Si empiezo con:

https://www.peterspython.com

entonces el SERVER_NAME se establece en: www.peterspython.com. Pero si empiezo con:

https://peterspython.com

entonces el SERVER_NAME se establece en: peterspython.com.

En ambos casos, config recuerda el SERVER_NAME entre las peticiones (de esta sesión), así que si cambio un URL de trabajo 'with-www' a un URL 'without-www', entonces obtenemos los errores 404 porque el enrutamiento Flask utiliza un SERVER_NAME erróneo.

El comportamiento de Flask de SERVER_NAME está bien documentado pero comprenderlo completamente es otra cosa. Mi problema también se describe en el artículo "Cosas que debes saber sobre Flask SERVER_NAME", ver enlaces abajo:

"Una vez que se establece SERVER_NAME, Flask sólo puede servir la solicitud de un solo dominio y devolver 404 para otros dominios. Si SERVER_NAME = midominio.com, no servirá la solicitud de www.mydomain.com ...'

Solución

La solución es, por supuesto, eliminar la línea:

        ....
        if current_app.config.get('SERVER_NAME') is  None:
        ....

Esto siempre establecerá la SERVER_NAME config variable en cada solicitud. O mejor aún (?), eliminar estas líneas todas juntas porque no uso SERVER_NAME en ninguna parte.

Resumen

En un momento dado añadí la variable config a mi código y establecí esta variable en before_request sólo cuando no estaba ya establecida. Olvidé que mi sitio web podía funcionar tanto con 'with-www' como con 'without-www' URLs y que la Flask config es persistente entre peticiones de la misma sesión. Una vez que llegué allí fue fácil de resolver.

Esto nunca hubiera pasado si hubiera elegido un solo canonical name para mi sitio web. Con este canonical name Flask sólo tendría que tratar con 'with-www' o 'without-www' pero nunca con ambos. En un próximo post explicaré por qué su canonical name debería ser siempre 'with-www'.

De todos modos, las lecciones aprendidas... otra vez.

Enlaces / créditos

SERVER_NAME configuration should not implicitly change routing behavior. #998
https://github.com/pallets/flask/issues/998

Things You Should Know About Flask SERVER_NAME
https://code.luasoftware.com/tutorials/flask/things-you-should-know-about-flask-server-name/

Unexplainable Flask 404 errors
https://stackoverflow.com/questions/24437248/unexplainable-flask-404-errors

Leer más

Flask

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios (2)

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.

avatar

This has been clawing at me all afternoon. Thank you for sharing this.

avatar

A word of warning, changing the app config every before_request is sensitive to race conditions. So if you are using a threaded model of Flask execution, I would strongly advise against using the presented approach.
A small demo I tried to see if it would fit my needs in this gist: https://gist.github.com/eelkevdbos/14177eb9d72f5c96ed0f22ed64c30d19