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

Flask's SERVER_NAME, sous-domaines et erreurs 404

Le paramétrage de Flask peut donner 404 erreurs si vous utilisez des sous-domaines.

25 novembre 2020
Dans Flask
post main image
https://unsplash.com/@azhar93

Voici un court article sur la variable Flask et la variable config variable SERVER_NAME. Comme beaucoup de développeurs, je suis tombé sur cette question à un moment donné, et j'ai pensé partager mon histoire. J'espère que cela évitera des maux de tête à certains.

Mes sites web doivent être accessibles en tapant les adresses suivantes dans le navigateur :

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

De plus, pour ce site, j'avais décidé que les URL des pages générées devaient être "without-www" ou "with-www", selon l'URL de la demande. Cela signifie que si je tape :

https://example.com/contact

il n'y a pas de redirection et tous les liens dans la page générée commenceront (sembleront) par https://example.com. Ou, si je tape :

https://www.example.com/contact

il n'y a pas de redirection et tous les liens dans la page générée commenceront (sembleront) par https://www.example.com.

Je dois ajouter que ce n'est pas la meilleure pratique et que vous devez toujours utiliser un seul canonical name, ce qui signifie que votre site web est soit 'with-www' soit 'without-www', et jamais les deux ! Dans un prochain article, j'entrerai dans les détails à ce sujet.

Quoi qu'il en soit, ce qui précède a bien fonctionné pour tous mes sites web sauf un, ce site web en fait. Il y a quelques mois, ce n'était pas un problème, mais cela a soudainement changé après une importante mise à jour du code. Pour éviter les erreurs 404 pour la version "with-www", j'ai résolu ce problème rapidement en faisant rediriger " Nginx " vers "without-www". Ensuite, d'autres projets plus urgents ont vu le jour et des mois ont passé.

Récemment, j'ai mis à jour mon serveur (ISPConfig) et j'ai pensé réessayer. Bien sûr, il y a encore 404 erreurs pour la version "with-www".

J'ai pris un peu de temps et j'ai décidé de créer la même situation sur mon PC de développement. Cela signifiait copier les certificats Letsencrypt sur mon PC et modifier légèrement la configuration de développement en changeant FLASK_ENV vers 'production' et en supprimant la redirection "with-www" vers "without-www" dans Nginx. J'ai laissé FLASK_DEBUG=True pour pouvoir mettre des points d'arrêt (exceptions) dans le code Python et voir ce qui se passait.

Heureusement, j'ai rencontré les mêmes erreurs 404 en passant de "without-www" à "with-www" sur mon PC. Puis, après un certain temps, j'ai redémarré Flask et soudain, la version "with-www" a fonctionné et la version "without-www " a donné 404 erreurs. Quoi ? L'inverse ?

En cherchant sur Internet la variable "Flask 404", je suis tombé sur des pages, voir les liens ci-dessous, qui laissaient entendre que la variable config variable SERVER_NAME pouvait être le problème.

Je me suis ensuite souvenu d'avoir ajouté SERVER_NAME à mon config lorsque je travaillais sur les tâches Celery . Je l'ai fait parce que je pensais qu'il pourrait être utile de générer des modèles Jinja avec des liens url_for() dans certaines tâches. Plus tard, j'ai écarté cette idée. Les tâches devraient être simples et ne pas contenir de clochettes. Mais je n'ai pas supprimé le code SERVER_NAME , car tout fonctionnait bien, et après avoir défini la redirection dans Nginx , il y avait d'autres projets plus urgents, ce qui est nouveau.

Description de ce qui s'est passé

Mon factory.py (__init__.py) ressemblait à :

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
        ....

Ici, j'obtiens SERVER_NAME de la requête si elle n'a pas été définie. Cela signifie qu'une fois que config[SERVER_NAME] est défini, il n'est plus mis à jour. Si je commence par :

https://www.peterspython.com

alors le SERVER_NAME est réglé sur : www.peterspython.com. Mais si je commence par :

https://peterspython.com

alors le SERVER_NAME est réglé sur : peterspython.com.

Dans les deux cas, config se souvient de SERVER_NAME entre les demandes (de cette session), donc si je change une URL "with-www" en une URL "without-www", nous obtenons alors les erreurs 404 parce que le routage Flask utilise un mauvais SERVER_NAME.

Le comportement du Flask est bien documenté, mais le comprendre pleinement est une autre chose. Mon problème est également décrit dans l'article "Ce que vous devez savoir sur Flask SERVER_NAME", voir les liens ci-dessous :

Une fois que vous avez défini SERVER_NAME, Flask ne peut servir que les demandes provenant d'un seul domaine et renvoyer 404 pour les autres domaines. Si SERVER_NAME = mondomaine.com, il ne servira pas la demande provenant de www.mydomain.com ...

Solution

La solution est bien sûr de supprimer la ligne :

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

Cela permettra toujours de définir la variable SERVER_NAME config variable sur chaque demande. Ou mieux encore ( ?), supprimer toutes ces lignes ensemble parce que je n'utilise pas la variable SERVER_NAME nulle part.

Résumé

À un moment donné, j'ai ajouté la variable config variable SERVER_NAME à mon code et j'ai défini cette variable dans before_request uniquement lorsqu'elle n'était pas déjà définie. J'ai oublié que mon site web pouvait fonctionner avec les URL "with-www" et "without-www" et que la variable Flask config est persistante entre les requêtes de la même session. Une fois que je suis arrivé, c'était facile à résoudre.

Cela ne serait jamais arrivé si j'avais choisi un seul canonical name pour mon site web. Avec ce canonical name , Flask n'aurait à s'occuper que de "with-www" ou de "without-www", mais jamais des deux. Dans un prochain article, j'expliquerai pourquoi votre canonical name devrait toujours être "with-www".

Quoi qu'il en soit, les leçons apprises ... encore une fois.

Liens / crédits

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

En savoir plus...

Flask

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires (2)

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.

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