Servidor web NGINX

En aquesta pràctica ens iniciarem amb les tecnologies que s'utilitzen per al desenvolupament d'aplicacions web.

World Wide Web

El científic anglés Tim Berners-Lee va inventar la World Wide Web (WWW), Xarxa d'Abast Mundial, en 1989, mentre treballava al CERN.

Coneguda comunament com la Web, és un sistema d'informació on documents i altres recursos són identificats amb URLs (Uniform Resource Locators), com, per exemple, http://wikipedia.org, que poden ser enllaçats amb hypertext, i són accessibles a través d'Internet.
Els recrsos de la Web són transferits pels Servidors Web via l' Hypertext Transfer Protocol (HTTP) i es pot accedir a ells utilitzant el Navegador Web.

URL

Com hem dit, les URLs són identificadors de recursos. Els recursos poden ser molt diversos: pàgines, imatges, videos, fitxers, etcètera. Per exemple:

Una URL es compon de 7 components:

Anem a veure amb més detall aquests components.

Scheme

El protocol d'accés al recurs indica la forma concreta en que s'han de comunicar el que fa la petició i el que la respón.

Per exemple, el protocol http serveix per a comunicar amb un servidor web. La especificacio del protocol és molt extensa, però podem veure un exemple senzill.

En la seva forma més utilitzada, HTTP serveix per a obtenir recursos.

Suposem que volem obtenir la pàgina principal del CERN. Aleshores, hem de realitzar un petició HTTP al seu servidor (info.cern.ch). Les dades que enviarem al servidor han de seguir exactament el protocol.

Utilitzarem el programa nc per a comunicar amb el servidor del CERN.

Obre un terminal i executa la següent comanda:

@host nc info.cern.ch 80

Amb aquesta comanda hem connectat al port 80 del servidor del CERN.
Ara el servidor està esperant que li enviem dades.

Si enviem qualsevol dada al servidor, aquest no serà capaç de comprendre la petició. Li hem de enviar les dades adequades, seguint el protocol HTTP.

Prova a escriure alguna cosa, i polsa Intro per a enviar les dades al servidor.

El servidor respondrà amb un error 400 (Bad request). La petició no ha estat correcta.

Per a fer la petició correcta hem fer el següent:

  1. Connectem amb el servidor:

    nc info.cern.ch 80
  2. Escrivim la comanda GET,
    després el recurs que volem /index.html
    després la versió del protocol HTTP/1.0
    després un "retorn de carro" Ctrl + V Ctrl + M
    i per últim un "retorn de línia" Intro

    GET /index.html HTTP/1.0 Ctrl + V Ctrl + M Intro
  3. Especifiquem el Host al qual estem connectant
    introduint Host: info.cern.ch
    "retorn de carro" Ctrl + V Ctrl + M
    i "retorn de línia" Intro

    Host: info.cern.ch Ctrl + V Ctrl + M Intro
  4. Indiquem al servidor que ha finalitzat la petició amb
    "retorn de carro" Ctrl + V Ctrl + M
    i "retorn de línia" Intro

    Ctrl + V Ctrl + M Intro

Aquest cop sí, el servidor ens respon amb 200 (OK), i ens retorna el recurs solicitat.

Aquesta petició que hem realitzat a mà és exactament el que fa el navegador web quan introduim la URL http://info.cern.ch a la barra d'adreçes.

Pots comprovar-ho de la següent forma:

  1. Obre una pestanya nova del Firefox

  2. Polsa F12 per a obrir les Eines de desenvolupador

  3. Ves a la pestanya

  4. Introdueix http://info.cern.ch/index.html a la barra d'adreçes i navega a la pàgina

  5. Firefox et mostrarà tots els recursos que està descarregant del servidor

  6. Selecciona el recurs index.html

  7. A la pestanya lateral , baixa fins a i activa l'opció

Ahí pots veure tota la petició HTTP que s'ha realitzat:

User

Alguns servidors requereixen d'autenticació amb usuari:contrasenya per a accedir a certs recursos

Instal·larem el servidor NGINX i realitzarem el configurarem per a requerir usuari:contrasenya en l'accés.

@server apt install -y nginx

Restricting Access with HTTP Basic Authentication

Primer necessitarem instal·lar el paquet apache2-utils. Aquest paquet conté el programa htpasswd que permet emmagatzemar usuaris i contrasenyes en un arxiu.

@server apt install apache2-utils

Creem l'usuari Joan:

@server htpasswd -c /var/www/contrasenyes joan

Comprovem que s'ha creat l'usuari:

@server cat /var/www/contrasenyes

Obrim el fitxer de configuració del lloc per defecte d'NGINX:

@server nano /etc/nginx/sites-enabled/default

Afegim les següents línies per a activar l'accés amb usuari/password:

/etc/nginx/sites-enabled/default server { ... auth_basic "Introdueix el teu usuari/password" auth_basic_user_file /var/www/contrasenyes ... }

Per últim reiniciem el servidor NGINX:

@server systemctl restart nginx

Per a accedir a la pàgina web haurem d'especificar un usuari i contrasenya vàlids

@host wget http://joan:joanpass@192.168.122.79

Host

El host és l'adreça IP o nom DNS del servidor.

Configurarem el servidor web per a que mostri dues pàgines diferents en funció del Host que se li indiqui en la petició.

Server names

Crearem dues carpetes (aaa.com i bbb.com), i en cadascuna d'elles posarem una web diferent

@server mkdir /var/www/html/aaa.com mkdir /var/www/html/bbb.com echo "<h1>welcome to aaa.com</h1>" > /var/www/html/aaa.com/index.html echo "<h1>welcome to bbb.com</h1>" > /var/www/html/bbb.com/index.html

Canviem la configuració d'NGINX per a que busqui els recursos que se li sol·licitin en una carpeta o l'altra en funció del Host (server_name) que s'indiqui a la petició http:

/etc/nginx/sites-enabled/default server { ... } server { listen 80; server_name aaa.com; root /var/www/html/aaa.com; } server { listen 80; server_name bbb.com; root /var/www/html/bbb.com; }

Realitzem dues peticions als diferents noms de host:

@host printf 'GET /index.html HTTP/1.0\r\nHost aaa.com\r\n\r\n' | nc IP_SERVIDOR 80 printf 'GET /index.html HTTP/1.0\r\nHost bbb.com\r\n\r\n' | nc IP_SERVIDOR 80

Observa que segons el Host indicat a la petició, es retorna una web o l'altra.

Si tractem de realitzar amb Firefox aquesta petició no ens serà possible. Si posem "aaa.com" o "bbb.com" a la barra d'adreces, Firefox tractarà de resoldre el nom DNS i no podrà. I si posem la IP del servidor web a la barra d'adreces, aleshores no estarem especificant el host a la petició.
Podem fer un apanyo temporal, i posar la resolucio dels noms "aaa.com" i "bbb.com" al fitxer /etc/hosts:

sudo nano /etc/hosts /etc/hosts ... aaa.com bbb.com

Port

NGNIX pot servir llocs web diferents en funció del port TCP del servidor al que es faci la petició.

Configurarem dos llocs diferents: un si es fa la connexió al port 80, i l'altre si es fa la connexió al port 1234

/etc/nginx/sites-enabled/default server { listen 80; root /var/www/html/aaa.com; } server { listen 1234; root /var/www/html/bbb.com; }

Accedim a http://ip_servidor:80

Accedim a http://ip_servidor:1234

Reestablim la configuració del lloc per defecte, per a deixar-la neta per al pas següent

printf "server {\n\troot /var/www/html;\n}\n" > /etc/nginx/sites-enabled/default

Path

Si no s'especifica cap arxiu en el path, per defecte, el servidor NGINX servirà l'arxiu index.html, o el que indiquem amb el paràmetre index de la configuració del lloc.

Anem a crear diversos arxius al directory /var/www/html:

@server echo "<h1>Home page</h1>" > /var/www/html/index.html echo "<h1>Pagina xxx </h1>" > /var/www/html/xxx.html echo "<h1>Pagina yyy </h1>" > /var/www/html/yyy.html

Si ara fem una petició al servidor sense indicar cap arxiu http://ip_servidor el servidor ens enviarà l'arxiu index.html

Si especifiquem algun arxiu a la petició (e.g. http://ip_servidor/xxx.html) Apache ens servirà aquest arxiu.

Si l'arxiu que posem a la url (e.g. http://ip_servidor/opq.html) no existeix, apache ens servirà una pàgina "Not Found"

Al path de la URL també es poden especificar els directoris on es troba l'arxiu que se solicita. NGINX buscarà aquest arxius a partir del root.

Per exemple, si el root és /var/www/html, i fem aquesta petició: http://ip_servidor/aaa/bbb/mmm.html, NGINX buscarà aquest arxiu /var/www/html/aaa/bbb/mmm.html. Comprovem-ho:

@server mkdir -p /var/www/html/aaa/bbb/ echo "<h1>Pagina mmm</h1>" > /var/www/html/aaa/bbb/mmm.html

Comprova que s'accedeix al recurs mmm.html amb aquesta URL http://ip_servidor/aaa/bbb/mmm.html.

Ara anem a canviar el root, i farem que sigui /var/www/html/aaa/:

/etc/nginx/sites-enabled/default server { ... root /var/www/html/aaa; }

Reiniciem el servidor:

@server systemctl restart nginx

Ara, per a accedir al recurs mmm.html, la URL que cal posar és http://ip_servidor/bbb/mmm.html.

Reestablim la configuració del lloc per defecte, per a deixar-la neta per al pas següent

printf "server {\n\troot /var/www/html;\n}\n" > /etc/nginx/sites-enabled/default

Query

La query serveix per a passar dades extra al recurs que s'està solicitant.

Veiem un exmple:

En aquesta url https://www.google.com/search?q=animals, estem passant al recurs search la dada extra q=animals. Aquest recurs ja sabrà que fer amb aquesta dada extra. Si li passem una query que el recurs no sap processar, normalment la ignorarà: https://www.google.com/search?jajaja=jejeje.

Anem a fer al nostre contenidor un recurs que accepti una query amb dades extra i faci alguna cosa amb aquestes dades.

Primer hem de comprendre que el recurs no pot ser una pàgina HTML, ja que el llenguatge HTML només és per a definir el contingut de la pàgina. És a dir, amb HTML no podem accedir a les dades de la query.

Un llenguatge que sí pot obtenir les dades de la query i fer alguna cosa amb aquestes dades es PHP. Anem a veure uns exemples simples amb el que es pot fer amb PHP.

Primer l'instal·lem:

@server apt install -y php php-fpm

Afegirem la següent configuració al lloc NGINX:

/etc/nginx/sites-enabled/default server { ... location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; include fastcgi.conf; } }

Configurem php-fpm per a escoltar al port 9000:

/etc/php/7.4/fpm/pool.d/www.conf ... listen = /run/php/php7.4-fpm.sock listen = 9000 ...

Reiniciem el servidor NGINX i PHP-FPM

@server systemctl restart php7.4-fpm nginx

Crearem un senzill script php que ens permetrà executar comandes al servidor a través del navegador web.

Anomenarem aquest recurs WEBASH (web + bash):

@server nano /var/www/html/webash.php

Afegim el següent codi a l'arxiu webash.php:

/var/www/html/webash.php <?php echo "<style> body { font-family: Consolas, monospace; }</style>"; echo "<span style='font-size: 2em;'>webash/</span>"; echo "<span>web&bash</span>"; ?>

Si accedim al recurs http://ip_servidor/webash.php veurem la pàgina generada per l'script webash.php:

De moment, el nostre codi PHP sempre mostra el mateix contingut, independentment de la query que li passem. És a dir, posis les dades que posis a la query, l'script les ignorarà. Per exemple: http://ip_servidor/webash.php?comanda=pstree, o http://ip_servidor/webash.php?hola=quetal

Anem a modificar l'script per que agafi una dada de la query i faci alguna cosa:

Afegim aquestes línies de codi a l'arxiu webash.php:

/var/www/html/webash.php <?php echo "<style> body { font-family: Consolas, monospace; }</style>"; echo "<span style='font-size: 2em;'>webash/</span>"; echo "<span>web&bash</span>"; echo "<h1>Hola "; echo $_GET['usuari']; echo "</h1>"; ?>

Ara posem la query usuari=gerard a la url: http://ip_servidor/webash.php?usuari=gerard. L'script php genera aquesta web:

Amb el codi $_GET['usuari'] de la línia 7, hem obtingut el valor de la dada usuari de la query, i l'hem utilitzat per a generar el contingut de l'etiqueta <h1>.

Si fas la query amb un altre valor per a la dada usuari, l'script generarà un altre codi html que inclourà aquest valor.

Per contra, si canvies la paraula usuari per una altra cosa, l'script ignorarà la dada.

Avançarem amb el webash, i ara farem que quan a la query se li passi una dada anomenada comanda, l'script agafarà el valor d'aquesta dada i l'executarà al sistema operatiu, retornant el seu resultat.

Esborra les línies anteriors, i afegeix aquestes línies:

/var/www/html/webash.php <?php echo "<style> body { font-family: Consolas, monospace; }</style>"; echo "<span style='font-size: 2em;'>webash/</span>"; echo "<span>web&bash</span>"; echo "<h1>Hola "; echo $_GET['usuari']; echo "</h1>"; echo "<pre style='width: 80ch; padding: 2ch; overflow: auto; color: white; background: #300A24;'>"; system("exec 2>&1 && " . $_GET['comanda']); echo "</pre>"; ?>

Ara l'script agafarà el valor de la dada anomenada comanda i l'executarà. Per exemple podem executar la pstree així: http://ip_servidor/webash.php?comanda=df -h:

La funció system executa el que se li posi entre els parèntesi, i el resultat de la comanda, s'inclou a la pàgina generada.

El que està executant aleshores la funció system és el valor de la dada comanda de la query. Prova a posar aquesta url http://ip_servidor/webash.php?comanda=cal o qualsevol altra comanda a la query.

Per últim, afegirem a webash una forma més còmoda de realitzar comandes, que no sigui posant-les a la url. Afegirem un formulari amb un camp de text i un botó:

/var/www/html/webash.php <?php echo "<style> body { font-family: Consolas, monospace; }</style>"; echo "<span style='font-size: 2em;'>webash/</span>"; echo "<span>web&bash</span>"; echo "<form>"; echo "<input name='comanda'>"; echo "<input type='submit' value='Executar'>"; echo "</form>"; echo "<pre style='width: 80ch; padding: 2ch; overflow: auto; color: white; background: #300A24;'>"; system($_GET['comanda']); echo "</pre>"; ?>

Ara ens serà més còmode executar una comanda:

Quan es premi el botó Executar, automàticament el navegador web agafarà els camps del <form> i els afegirà a la query de la petició. Anomenarà les dades de la query segons l'atribut name dels <input> i el valor serà el que s'hagi escrit en ells. Prova a executar algunes comandes!

Fragment

El fragment serveix per a fer que el navegador posicioni la pàgina en una secció determinada. Aquesta part de la url no arriba al servidor, és el navegador el qui ha de saber com gestionar el fragment.

Veiem un exmple:

Generem un arxiu html extens amb 5 seccions (id=sX) i 30 paràrafs en cada secció:

@server for i in {1..5}; do printf "<h1 id='s$i'>Seccio $i</h1>; for j in {1..30}; do printf "<p>seccio $i, paràgraf $j</p>"; done; done > /var/www/html/llarg.html

Prova a navegar directament a alguna de les seccions del document.

Per a definir una secció en un document hem d'afegir l'atribut id a l'element que desitjem. Per a navegar directament a una secció, només cal afegir un # seguit del valor del seu atribut id.

Servidor web

El servidor web és un software que respon a les peticions dels clients en la WWW. Un servidor web pot tenir un o més llocs web.

Hi ha una àmplia varitat de software que s'utilitza com a servidor web. Els que més quota de mercat tenen actualment són: Apache (44.3%) i nginx (41%).

Servir recursos

La tasca principal d'un servidor web és respondre a les peticions HTTP amb els recursos solicitats.

Els recursos que entrega el servidor web als clients són freqüentment fitxers amb codi HTML, codi CSS i codi Javascript. També pot enviar altres tipos de recursos com imatges, videos i arxius de tot tipus.

Veiem un exemple. Descarrega aquest vídeo de Tim Berners-Lee al directory /var/www/html del servidor:

@host wget -O /var/www/html/tim.webm https://upload.wikimedia.org/wikipedia/commons/7/7b/What_is_the_future_of_the_internet-_-_Tim_Berners_Lee.webm

Utilitza el client Firefox per a fer una petició d'aquest recurs al servidor: http://10.2.4.100/tim.webm.

Estàtic vs Dinàmic

Un servidor web pot servir contingut estàtic o dinàmic:

Veiem un exemple: Serverdate

Crea aquest arxiu al servidor:

/var/www/html/serverdate.html <!DOCTYPE html> <p>La data del servidor es: Tue Oct 20 10:13:08 UTC 2020</p>

Navega a http://10.2.1.24/serverdate.html

El recurs serverdate.html és un recurs estàtic. El servidor web el servirà tal qual, per això encara que recarregis la pàgina, la data/hora no canviarà.

Provem a fer un continugt dinàmic:

@server apt install -y php php-fpm

Afegirem la següent configuració al lloc NGINX:

/etc/nginx/sites-enabled/default server { ... location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; include fastcgi.conf; } }

Configurem php-fpm per a escoltar al port 9000:

/etc/php/7.4/fpm/pool.d/www.conf ... listen = /run/php/php7.4-fpm.sock listen = 9000 ...

Reiniciem el servidor NGINX i PHP-FPM

@server systemctl restart php7.4-fpm nginx

Escriu aquest script PHP:

/var/www/html/serverdate.php <?php echo "<p>La data del servidor es: "; system(date); echo "</p>"; ?>

Ara, cada vegada que navegues al recurs http://10.1.2.23/serverdate.php el servidor web executa l'script i envia al client el recurs resultant. La comanda system(date) obté l'hora actual del servidor, per tant cada vegada que recarreguis la pàgina, obtindràs l'hora actual.

Base de dades

La majoria d'aplicacions web utilitzen una base de dades per a emmagatzemar la informació que han de mostrar. Els scripts poden guardar i obtenir la informació d'elles (com per exemple, les publicacions que fan els usuaris a una xarxa social).

Farem una aplicació web anomenada TheWall on els visitants podran escriure una firma, i totes les firmes que es vagin posant es mostraran a la pàgina.

Instal·la el gestor de bases de dades MySQL:

@server wget https://repo.mysql.com//mysql-apt-config_0.8.18-1_all.deb apt install -y ./mysql-apt-config_0.8.18-1_all.deb apt update apt install mysql-server

Crearem una base de dades per a la app TheWall:

@server mysql -e "CREATE DATABASE thewalldb"

Ara crearem un usuari per a administrar aquesta base de dades:

@server mysql -e "CREATE USER 'thewalldbadmin'@'localhost' IDENTIFIED BY 'pass123'"

Li donem privilegis a l'usuari thewalldbadmin per a administrar la base de dades thewalldb:

@server mysql -e "GRANT ALL PRIVILEGES ON thewalldb.* TO 'thewalldbadmin'@'localhost'; FLUSH PRIVILEGES"

El més habitual és que les bases de dades emmagatzemin la informació en taules. Per a la nostra app crearem una taula firmes on es s'aniran guardant les firmes dels visitants. Aquesta taula només tindrà un camp que contindrà una firma.

@server mysql -e "CREATE TABLE thewalldb.firmes(firma TEXT)"

Insertarem unes quantes firmes de mostra a la taula firmes:

@server mysql -e "INSERT INTO thewalldb.firmes VALUES('hola que tal')" mysql -e "INSERT INTO thewalldb.firmes VALUES('pasaba por aqui')" mysql -e "INSERT INTO thewalldb.firmes VALUES('jajajaaajaaa')" mysql -e "INSERT INTO thewalldb.firmes VALUES('que hay que poner aqui?')"

Podem veure que les firmes s'han introduït a la taula firmes així:

@server mysql -e "SELECT * FROM thewalldb.firmes"

Ara el següent pas es programar un script PHP que obtingui la informació de la base de dades i la posi en la pàgina web que generi.

Insal·larem el paquet php-mysql que permet connectar a una base de dades MySQL des d'un script PHP:

@server apt install -y php-mysql

L'script que connecta a la base de dades thewalldb, obté totes les firmes i les volca a la pàgina és aquest:

/var/www/html/thewall.php <style> p, input { font-family: sans; padding: 1em; border-radius: .75em; border: 1px solid #deaeae;} </style> <?php $db = new mysqli("localhost", "thewalldbadmin", "pass123", "thewalldb"); foreach ($db->query("SELECT * FROM firmes") as $fila) { echo "<p>".$fila['firma']; } ?>

Naveguem a http://ip_servidor/thewall.php i veurem com l'script ha volcat totes les firmes que hi ha a la base de dades:

Aquest contingut és dinàmic: depèn del contingut de la base de dades. Ho podem comprovar afegint més firmes a la base de dades:

@server mysql -e "INSERT INTO thewalldb.firmes VALUES('te quiero')" mysql -e "INSERT INTO thewalldb.firmes VALUES('que pasa?')" mysql -e "INSERT INTO thewalldb.firmes VALUES('nunca te olvidare')"

Acabarem l'script thewall permetent que els visitants escriguin la seva firma. Aquesta firma s'enviarà a l'script en la query, i l'script l'obtindrà i la guardarà a la base de dades.

/var/www/html/thewall.php <style> p, input { font-family: sans; padding: 1em; border-radius: .75em; border: 1px solid #deaeae;} </style> <form> <input name="firma"> <input type="submit" value="Firmar"> </form> <?php $db = new mysqli("localhost", "thewalldbadmin", "pass123", "thewalldb"); if (isset($_GET["firma"])) { $stmt = $db->prepare("INSERT INTO firmes VALUES(?)"); $stmt->bind_param("s", $_GET["firma"]); $stmt->execute(); $stmt->close(); } foreach ($db->query("SELECT * FROM firmes") as $fila) { echo $fila['firma']; } ?>

NGINX Config

rewrite

http://nginx.org/en/docs/http/ngx_http_rewrite_module.html

/etc/nginx/sites-enabled/default server { root /var/www/html; location / { rewrite ^/$ /index.php last; rewrite ^(.*)$ $1.php last; } location ~ \.php$ { fastcgi_pass backend; include fastcgi.conf; } }

Load balancing

/etc/nginx/sites-enabled/default upstream backend { server 192.168.122.XXX:9000; server 192.168.122.YYY:9000; } server { root /var/www/html; location ~ \.php$ { fastcgi_pass backend; include fastcgi.conf; } }

https

Generar el certificat i la clau privada

@server openssl req -x509 -nodes -days 365 -newkey rsa:2048 -subj '/CN=192.168.122.XXX' -keyout /etc/ssl/private/site.key -out /etc/ssl/certs/site.pem /etc/nginx/sites-enabled/default server { listen 443 ssl; ssl_certificate /etc/ssl/certs/site.pem; ssl_certificate_key /etc/ssl/private/site.key; ... }

Protocol http

Method

POST

<!DOCTYPE html> <form method="POST"> <input type="text" name="identificador"> <input type="submit"> </form> <?php echo "<h1>Hola, ".$_POST["identificador"]."!</h1>" ?>

POST multipart/formdata

<!DOCTYPE html> <meta charset="UTF-8"> <title>File Manager</title> <style> html { font-family: sans; } h1 { color: gray; border-bottom: 1px solid gray; padding-bottom: 16px; } a { text-decoration: none; color: inherit; } .file-grid { display: grid; grid-template-columns: repeat(auto-fill, 200px); grid-template-rows: repeat(auto-fill, 250px); grid-gap: 1em; } .file { display: flex; flex-direction: column; justify-content: flex-end; } .file a { display: flex; flex-direction: column; align-items: center; } .file p { text-align: center; } img { max-width: 80%; max-height: 80%; box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); } label { background-color: #55acee; color: white; border: 0px; border-radius: 4px; padding: 16px; margin-bottom: 24px; } .trash { background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgdmlld0JveD0iMCAwIDk2IDk2IiB4PSIwcHgiIHk9IjBweCI+PHRpdGxlPkFydGJvYXJkIDY2PC90aXRsZT48cG9seWdvbiBwb2ludHM9IjQwLjc4IDY2IDU1LjIyIDY2IDU3Ljc2IDQ0IDM4LjI0IDQ0IDQwLjc4IDY2Ij48L3BvbHlnb24+PHBvbHlnb24gcG9pbnRzPSI0NC4yNCAzMSA0My4yNCAzMyA1Mi43NiAzMyA1MS43NiAzMSA0NC4yNCAzMSI+PC9wb2x5Z29uPjxwYXRoIGQ9Ik00OCwyQTQ2LDQ2LDAsMSwwLDk0LDQ4LDQ2LDQ2LDAsMCwwLDQ4LDJaTTYyLDQyLjIzbC0zLDI2QTIsMiwwLDAsMSw1Nyw3MEgzOWEyLDIsMCwwLDEtMi0xLjc3bC0zLTI2YTIsMiwwLDAsMSwuNS0xLjU2QTIsMiwwLDAsMSwzNiw0MEg2MGEyLDIsMCwwLDEsMS40OS42N0EyLDIsMCwwLDEsNjIsNDIuMjNaTTYzLDM3SDMzYTIsMiwwLDAsMSwwLTRoNS43NmwyLjQ1LTQuODlBMiwyLDAsMCwxLDQzLDI3SDUzYTIsMiwwLDAsMSwxLjc5LDEuMTFMNTcuMjQsMzNINjNhMiwyLDAsMCwxLDAsNFoiPjwvcGF0aD48L3N2Zz4=); background-size: contain; width: 30px; height: 30px; display: block; position: relative; top: -80px; left: -6px; } input[type=file] { display: none; } </style> <h1>&#128194; File manager</h1> <form id="upload-form" enctype="multipart/form-data" method="POST"> <input id="file" name="userfile" type="file" onchange="document.getElementById('upload-form').submit()"/> <label for="file">Upload file...</label> </form> <?php if(isset($_FILES['userfile'])){ move_uploaded_file($_FILES['userfile']['tmp_name'], 'uploads/' . $_FILES['userfile']['name']); } if(isset($_GET['delete'])){ unlink('uploads/' . $_GET['delete']); } ?> <div class="file-grid"> <?php if ($handle = opendir('uploads/')) { while (($file = readdir($handle))) { if ($file != "." && $file != "..") { echo " <div class='file'> <a href='uploads/$file'> <img src='uploads/$file'> <p>$file</p> </a> <a class='trash' href='?delete=$file'></a> </div> "; } } closedir($handle); } ?> </div>

Cookies

https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies

nc -l 8000 HTTP/1.0 200 OK Content-Type: text/html Set-Cookie: una_cookie=choco Set-Cookie: una_altra_cookie=strawberry <a href="/">Tornar</a> <h1>Hello</h1> <form> <input type="color" name="favcolor"> <input type="submit"> </form> <?php if(isset($_GET['favcolor'])){ setcookie("color", $_GET['favcolor']); } ?> <style> body { background: <?php echo $_COOKIE['color'] ?> } </style>

Location

nc -l 8000 HTTP/1.0 303 OK Location: https://wikipedia.org <?php header("location: login.php"); ?>