programmazione

Un errore subdolo: Apache negotiation e Options +MultiView

Se non ti sei mai imbattuto in questo errore ma per il timore che succeda vuoi risparmiare ore preziose...

Andrea Scianò Andrea Scianò 31 Mar 2018 · lettura da 5 min
Un errore subdolo: Apache negotiation e Options +MultiView

In uno dei post passati spiegavo come questo blog fosse una SPA (Single Page Application) sviluppato peraltro con Ember 2. La scelta del framework utilizzato fu dettata dalla voglia di sperimentare una delle ultime versioni dello stesso, perchè quella usata a lavoro era un poco più vecchia.

Dopo alcune valutazioni però, mi sono deciso a cambiare, rifacendo il tutto con tecnologie probabilmente molto meno potenti ma più consone a ciò di cui avevo bisogno. Ora sono semplicemente pagine PHP generate lato server.

Ho letto bene!? PHP!? 😱

Si avete letto bene, prima di insultarmi lasciatemi ricordare che sono uno sviluppatore frontend e gli unici due linguaggi classici lato server che conosco sono Java e PHP. Quest'ultimo mi permetteva di contenere i costi per l'hosting, in più ritenevo che un blog del genere non avesse richiesto strumenti avanzati come il solo Java sa offrire, per lo meno fin quando non cambierò nuovamente idea. 😊 Per poter essere il più veloce possibile optai per la seconda opzione.

Perchè cambiare?

Detto questo, il principale motivo di cambiamento fu solamente uno: Facebook! Ebbene si, dopo aver finito l'applicazione in Ember 2, scoprii che quando si tenta di condividere un post, il crawler di Facebook controlla che vi siano alcuni metadati dentro il tag head dell'HTML per poter creare l'anteprima.

Parliamo del quadratino con foto, titolo e breve descrizione; si, proprio quello a cui molto spesso, per questioni di Clickbait, viene assegnato un titolo roboante e spesso fittizio, con la scusa di creare curiosità ed aprirlo.
Peccato però che da questo fenomeno siano nate le fake news, siccome quasi sempre, la maggior parte della popolazione, la quale è composta pressochè da analfabeti funzionali, tende a guardare solamente il titolo, senza poi cliccare e leggere l'intero articolo per farsi un'idea più chiara di quanto riporta la notizia. Ma aldilà di questa parentesi, quando lo scoprii pensai:

Poco male, genero i meta tag dinamicamente a seconda di che articolo viene selezionato!

Peccato che il caro Mark (il creatore di FB, ndr) richieda che tali tag siano generati lato server! Io apparentemente non avevo questa possibilità (ricordate che le SPA stanno sul client giusto?) ed è proprio in quel momento che assunsi la stessa postura della statua Facepalm presente a Parigi, nel Giardino delle Tuileries - la potete ammirare qui sopra come foto del post. 😄☝️

Vi è la possibilità di usare SPA con rendering fatto dal server, ma con Ember non fu così agevole, dunque optai per una soluzione ibrida: scrissi del codice che mi generava lato server la pagina generica index, compreso il tag head con i metadati necessari, ed una volta sul client, la SPA veniva iniettata nel body di quella pagina. Lo so, un po' contorto ma funzionava. Ed avrebbe continuato a funzionare, però non mi convinse mai come soluzione (per chi lo desidera ho il codice, basta chiedere) ed inoltre ogni qualvolta che dovevo caricare un nuovo articolo, causa questo artefizio, la routine non era così agevole.

Dannato Options +MultiView

Finito di riscrivere l'applicazione, mi accingo a ricaricarla sul server nella nuova modalità. Tutto pare funzionare come in locale ma non appena clicco su di un qualsiasi articolo, il server mi risponde con 404.
Strano, penso io, ed inizio a controllare che la configurzione del DB sia corretta. Lo era, altrimenti non avrei visualizzato nemmeno la prima pagina, la quale invece era mostrata correttamente.

Intuisco che qualcosa non sta funzionando come in locale, e mi soffermo sul file .htaccess.
Avevo re-implementato la riscrittura degli URL per avere uno schema orientato alle risorse, umanamente leggibile ed intuitivo:

www.sciano.net?index.php?page=post&id=9&title=che-bel-post

sarebbe diventato:

www.sciano.net/post/9/che-bel-post

In locale funzionava tutto perfettamente! Come era possibile?
Dopo un sacco di ricerche su Google, molta frustrazione, svariati tentativi cambiando RewriteRule, decido di dare un'occhiata al log del server (prima lezione, fatelo prima).
Non appena lo apro leggo il seguente messaggio:

Negotiation: discovered file(s) matching request: ... (None could be negotiated)

Mai visto prima, ma per lo meno avevo un indizio in più.
Mi metto nuovamente a googlare e scopro che una fantastica opzione, chiamata MultiView, può andare in conflitto se non usata nella maniera corretta con la riscrittura degli URL. La documentazione diceva:

🇺🇸 If the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files, assigning them the same media types and content-encodings it would have if the client had asked for one of them by name. It then chooses the best match to the client's requirements.

🇮🇹 Se il server riceve una richiesta per /qualche/cartella/foo, se qualche/cartella ha MultiViews abilitato, e /qualche/cartella/foo non esiste, allora il server legge la cartella cercando file chiamati foo.*, ed efficacemente simula una mappa dei tipi che denomina tutti quei file, assegnandogli gli stessi tipi di media e codifiche del contenuto che avrebbe se il client avesse richiesto uno di essi per nome. Quindi sceglie la migliore corrispondenza alle esigenze del client.

Ed era esattamente quello che stava succedendo: andando all'URL /post ma non esistendo tale risorsa, Apache trovava il file post.php (più di uno in realtà) ed usava Content Negotiation per cercar di capire quale file avrebbe dovuto fornire in base agli header della richiesta.
Non essendo però presente il tipo php nella configurazione che mappa i tipi di file da restituire, Apache non sapeva come rispondere e ritornava un 404!

Aggiungendo l'istruzione Options -MultiViews nel file .htaccess prima delle regole di rewrite, la quale toglie l'opzione MultiViews (notare il segno meno davanti) fixava perfettamente il problema. 💪

Conclusioni 🏁

Due cose ho imparato personalmente da questa esperienza, la prima è: leggere i log anzichè provare soluzioni estemporanee può farti risparmiare un sacco di tempo (si, lo so che sono noiosi).
Quanto sia importante conoscere l'ambiente in cui si lavora, anche a basso livello, evitando di abilitare/disabilitare opzioni senza conoscere l'impatto che esse hanno nel contesto in cui si trovano, è la seconda.

Indovinello ⁉️

Cosa sarebbe accaduto, anzichè rispondere 404, se nella mappatura dei file fosse stato presente il formato php? 😁 Ok, lo so che non ho ancora aggiunto i commenti al blog (WIP) per ora potete commentare su FB o scrivermi la risposta ☺️.