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

Flask's SERVER_NAME, subdomeinen en 404 fouten

Het instellen van Flask's SERVER_NAME kan 404 fouten opleveren als u subdomeinen gebruikt.

25 november 2020
In Flask
post main image
https://unsplash.com/@azhar93

Dit is een kort bericht over Flask en de config variabele SERVER_NAME. Zoals veel ontwikkelaars kwam ik dit op een bepaald moment tegen en ik dacht dat ik mijn verhaal zou delen. Hopelijk voorkomt dit voor sommigen hoofdpijn.

Mijn websites moeten beschikbaar zijn door de volgende adressen in de browser in te typen:

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

Wat meer is, voor deze website had ik besloten dat de URL's in de gegenereerde pagina's 'without-www' of 'with-www' moeten zijn, afhankelijk van wat de aanvraag-URL was. Dit betekent dat als ik typ:

https://example.com/contact

er is geen omleiding en alle links in de gegenereerde pagina zullen (lijken) beginnen met https://example.com. Of, als ik typ:

https://www.example.com/contact

er is geen omleiding en alle links in de gegenereerde pagina zullen (lijken) beginnen met https://www.example.com.

Ik moet hieraan toevoegen dat dit niet de beste werkwijze is en dat u altijd een enkele canonical name moet gebruiken, wat betekent dat uw website ofwel 'with-www' of 'without-www' is, en nooit allebei! In een volgende post zal ik hierover in detail treden.

Hoe dan ook, het bovenstaande werkte prima voor al mijn websites, op één na, deze website in feite. Maanden geleden was dit geen probleem, maar na een grote code-update veranderde het ineens. Om 404 fouten voor de 'with-www' versie te voorkomen heb ik dit snel opgelost door ' Nginx ' om te leiden naar 'with-www '. En toen waren er dringender andere projecten en maanden voorbij gegaan.

Onlangs was ik mijn (ISPConfig) server aan het updaten en dacht ik dit opnieuw te proberen. Natuurlijk zijn er nog steeds 404 fouten voor de 'with-www' versie.

Ik heb wat tijd gemaakt en heb besloten om dezelfde situatie te creëren op mijn ontwikkelings-PC. Dit betekende dat ik de Letsencrypt certificaten naar mijn PC moest kopiëren en de ontwikkelingsconfiguratie enigszins moest aanpassen door het wijzigen van FLASK_ENV naar 'production' en het verwijderen van de 'with-www' naar 'without-www' omleiden in Nginx. Ik heb FLASK_DEBUG=True verlaten zodat ik breekpunten (uitzonderingen) in de code Python kon zetten en kon zien wat er aan de hand was.

Gelukkig ondervond ik dezelfde 404 fouten bij het veranderen van 'without-www' in 'with-www' op mijn PC. Na enige tijd heb ik Flask opnieuw opgestart en nu werkte de 'with-www' versie ineens en de 'without-www' versie gaf 404 fouten. Wat? Andersom?

Bij het zoeken op internet naar 'Flask 404' kwam ik op pagina's terecht, zie onderstaande links, die erop wezen dat de config variabele SERVER_NAME het probleem zou kunnen zijn.

Toen herinnerde ik me dat ik SERVER_NAME aan mijn config toevoegde toen ik aan Celery werkte. Ik deed dit omdat ik dacht dat het nuttig kon zijn om Jinja sjablonen te genereren met url_for() links in sommige taken. Later heb ik dit idee verworpen. Taken moeten eenvoudig zijn en geen toeters en bellen bevatten. Maar ik heb de SERVER_NAME code niet verwijderd, omdat alles in orde was, en na het instellen van omleiding in Nginx waren er dringender andere projecten, wat anders nieuw is.

Beschrijving van wat er gebeurd is

Mijn fabriek.py (__init__.py) zag er zo uit:

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

Hier krijg ik SERVER_NAME van de aanvraag als deze niet is ingesteld. Dit betekent dat zodra config[SERVER_NAME] is ingesteld, deze niet meer wordt bijgewerkt. Als ik begin met:

https://www.peterspython.com

dan is de SERVER_NAME ingesteld op: www.peterspython.com. Maar als ik begin met:

https://peterspython.com

dan is de SERVER_NAME ingesteld op: peterspython.com.

In beide gevallen config onthoudt de SERVER_NAME tussen de verzoeken (van deze sessie) dus als ik een werkende 'with-www' URL verander in een 'without-www' URL, dan krijgen we de 404 fouten omdat Flask routing een verkeerde SERVER_NAME gebruikt.

Het gedrag van Flask's SERVER_NAME is goed gedocumenteerd maar begrijpt het volledig anders. Mijn probleem wordt ook beschreven in het artikel 'Dingen die je moet weten over Flask SERVER_NAME', zie onderstaande links:

Als u eenmaal SERVER_NAME hebt ingesteld, kan Flask alleen nog maar een aanvraag van een enkel domein dienen en 404 terugsturen voor andere domeinen. Als SERVER_NAME = mydomain.com, zal het niet dienen verzoek van www.mydomain.com ...'.

Oplossing

De oplossing is natuurlijk om de lijn te verwijderen:

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

Hiermee wordt altijd de SERVER_NAME config -variabele ingesteld op elk verzoek. Of nog beter (?), verwijder deze regels allemaal samen omdat ik SERVER_NAME nergens gebruik.

Samenvatting

Op een gegeven moment heb ik de config variabele SERVER_NAME aan mijn code toegevoegd en deze variabele alleen in before_request gezet als deze nog niet is ingesteld. Ik vergat dat mijn website kon draaien met zowel 'with-www' als 'without-www' URL's en dat de Flask config aanhoudend is tussen verzoeken van dezelfde sessie. Eenmaal daar was het gemakkelijk op te lossen.

Dit zou nooit gebeurd zijn als ik een enkele canonical name had gekozen voor mijn website. Met deze canonical name Flask zou alleen 'with-www' of 'without-www' hoeven te worden behandeld, maar nooit met beide. In een volgende post zal ik uitleggen waarom uw canonical name altijd 'with-www' zou moeten zijn.

Hoe dan ook, lessen geleerd ... opnieuw.

Links / credits

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

Lees meer

Flask

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen (2)

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.

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