Servidor Web Local con Raspberry PI

En este caso quiero montar un Servidor Web Local

Lo primero es disponer de una Raspberry PI, para este ejercicio tenemos una del modelo 3B con MicroSD de 32Gb.

Se ha descargado la última versión de Raspberry OS Lite x64, para convertila en un servidor web PHP + MariaDB + phpMyAdmin, solo en red local (por ahora), si todo va bien, en 20-30minutos, debería estar todo operativo…. pero no, os vais a olvidar de la contraseña de algo… y tendreis que volver a empezar. Palabrta.

🧱 FASE 0 — Suposiciones

Asumimos que:

  • Tienes conocimientos/experiendia en en este mundo
  • Ya has instalado el SO y activado el acceso por SSH y…
  • Ya entras por SSH
  • El sistema está actualizado
  • Estás como usuario con privilegios

🔹 FASE 1 — Actualizar sistema (base limpia)

sudo apt update 
sudo apt upgrade -y
sudo reboot

🔹 FASE 2 — Instalar NGINX (Servidor Web)

sudo apt install nginx -y

Comprobamos que funciona

Desde un PC en la misma red, con nuestro navegador preferido: http://IP_DE_LA_RASPI <– para conocer la IP, solo tienes que buscarla en tu router, o con AdvanceIP Scanner, pero si no, lo más rápido es conectarle un cable de video (HDMI) y al acabar de iniciarse, te muestra la IP asignada. (Más adelante, veremos para poner una IP fija).

Si todo ha ido bien… veríamos esto en nuestro Navagador:

📌 Guarda/Apunta esta ruta:

/var/www/html

🔹 FASE 3 — Instalar PHP + PHP-FPM (optimizado)

sudo apt install php php-fpm php-cli php-mysql php-json php-curl php-zip php-gd php-mbstring php-xml -y

Comprobamos versión

php -v

A día de hoy: 8.4

PHP 8.4.16 (cli) (built: Dec 18 2025 21:19:25) (NTS)
Copyright (c) The PHP Group
Built by Debian
Zend Engine v4.4.16, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.16, Copyright (c), by Zend Technologies

🔹 FASE 4 — Conectar Nginx con PHP

Editamos el site por defecto:

sudo nano /etc/nginx/sites-available/default

Buscamos esta línea (en mi caso, línea 43):

# Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

Y la dejamos tal que así:

# Add index.php to the list if you are using PHP
        index index.php index.html index.htm index.nginx-debian.html;

En el mismo archivo, activamos PHP (dentro del bloque server {}), SOLO DEBEMOS REITRAR LAS # y queraría tal que así:

 # pass PHP scripts to FastCGI server
        #
        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
                fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        }
⚠️ Si tu PHP es otra versión, compruébalo con: ls /run/php/

Reiniciamos servicios

sudo systemctl restart nginx
sudo systemctl restart php8.4-fpm

🔹 FASE 5 — Test rápido de PHP

sudo nano /var/www/html/info.php

Se abre un archivo vacío y escribimos este contenido:

<?php
phpinfo();

Desde el navegador:

http://IP_DE_LA_RASPI/info.php

✔️ Si ves la página de PHP → perfecto
Luego bórralo (por seguridad):

sudo rm /var/www/html/info.php

🔹 FASE 6 — Instalar MariaDB (MySQL)

sudo apt install mariadb-server mariadb-client -y
  1. Asegurar instalación:
sudo mysql_secure_installation

Respuestas recomendadas:

  • Cambiar root password →
  • Remove anonymous users →
  • Disallow root login remotely →
  • Remove test database →
  • Reload privilege tables →

2. Si el paso uno da error, lo haremos por otro camino:

1️⃣ Entra a MariaDB como root del sistema

sudo mariadb

Si entra sin pedir password → todo correcto.


2️⃣ Asegurar el usuario root (método moderno)

Ejecuta tal cual, una a una, sustituyendo «PASSWORD» por la que quieras, hazlo línea por línea:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'PASSWORD';
FLUSH PRIVILEGES;

📌 Usa una contraseña fuerte y guárdala.


3️⃣ Eliminar accesos inseguros

Hazlo línea por línea:

DELETE FROM mysql.user WHERE User='';
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;

4️⃣ Salir

EXIT;

🔎 Comprobación

Prueba:

mysql -u root -p

Si te pide contraseña y entra → OK.

🔐 FASE 6.1 SEGURIDAD

1️⃣ ❌ No usar «root» en PHP (NUNCA), por eso crearemos un «webuser»

Qué es root en MariaDB

root es:

  • Usuario total
  • Puede borrar TODAS las bases
  • Cambiar usuarios
  • Alterar permisos
  • Romper el sistema entero

Qué pasa si usas root desde PHP

Si una WebApp tiene:

  • Un bug
  • Un include mal hecho
  • Un eval
  • Un upload inseguro
  • Un SQL Injection

👉 El atacante hereda privilegios root
👉 Puede borrar TODAS las bases
👉 Puede crear usuarios ocultos
👉 Puede persistir

📌 Aunque sea red local, el riesgo existe (malware, dispositivos comprometidos, errores humanos).


En cambio, usando webuser

  • Acceso solo a sus bases
  • Sin privilegios administrativos
  • Daños limitados

Ejemplo ideal:

GRANT SELECT, INSERT, UPDATE, DELETE 
ON app1_db.* 
TO 'webuser'@'localhost';

🎯 Regla de oro

PHP = usuario limitado
Admin = root (solo consola)


2️⃣ No exponer MariaDB a la red

Qué significa “exponer”

Que MariaDB:

  • Escuche en 0.0.0.0
  • Acepte conexiones desde otras máquinas

Ejemplo peligroso:

bind-address = 0.0.0.0

Riesgos reales

  • Ataques de fuerza bruta
  • Exploits antiguos
  • Escaneos automáticos
  • Malware interno en la LAN

📌 MariaDB no tiene firewall propio
📌 Es un objetivo habitual


Configuración segura (la que queremos)

bind-address = 127.0.0.1

Resultado:

  • Solo PHP local accede
  • phpMyAdmin funciona
  • Nadie desde fuera entra

¿Y si algún día lo necesitas?

Entonces:

  • VPN
  • SSH tunnel
  • Firewall estricto

Nunca “abrir por abrir”.


🧑‍💻 3️⃣ ✅ Usar siempre webuser

Qué es webuser

Un usuario hecho para apps, no para admins.

Características:

  • Solo conecta desde localhost
  • Solo accede a sus BDs
  • Sin GRANT, DROP USER, etc.

Ejemplo realista

CREATE DATABASE app1_db;
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'password_seguro';

GRANT SELECT, INSERT, UPDATE, DELETE
ON app1_db.*
TO 'webuser'@'localhost';

FLUSH PRIVILEGES;

Ventaja clave en nuestro escenario (AIStudio de Google + JSON)

Mis futuros proyectos:

  • Guardan estado en JSON
  • Usan DB para metadatos

👉 webuser va sobrado
👉 Menos RAM
👉 Menos riesgo
👉 Más estabilidad

🔧FASE 6.2 CREAR webuser

1️⃣ Entra como root en mySQL

mysql -u root -p

2️⃣ Crear el usuario (solo local)

CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'PASSWORD_FUERTE';

📌 Usa una contraseña fuerte y guárdala.


3️⃣ Crear una base “contenedor” inicial (opcional)

Si quieres una base común:

CREATE DATABASE apps_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

(O luego hacemos una BD por app, sin problema).


4️⃣ Dar permisos limitados y correctos

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, ALTER
ON apps_db.*
TO 'webuser'@'localhost';

⚠️ NO damos:

  • DROP
  • GRANT OPTION
  • SUPER

5️⃣ Aplicar cambios

FLUSH PRIVILEGES;
EXIT;

🔎 Verificación (muy importante)

mysql -u webuser -p

Luego:

SHOW DATABASES;

Debe mostrar:

apps_db

Si es así → perfecto

Salimos:

exit;

🧠 Estado actual del sistema

Ahora tenemos:

  • Seguridad correcta incluso en LAN
  • root → solo administración
  • webuser → WebApps / phpMyAdmin
  • DB lista para trabajar

🔹 FASE 7 — Ajustar MariaDB para Raspberry Pi 3B

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Justo debajo de [mariadbd] ponermos:

# ===============================
# Optimización para Raspberry Pi
# ===============================

# Escuchar solo en localhost (SEGURIDAD)
bind-address = 127.0.0.1

# Conexiones
max_connections = 20
thread_cache_size = 8

# Buffers generales
key_buffer_size = 16M
table_open_cache = 200
table_definition_cache = 200

# InnoDB (ajustado a 1GB RAM)
innodb_buffer_pool_size = 64M
innodb_log_buffer_size = 8M
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 2

# Timeouts
wait_timeout = 300
interactive_timeout = 300

# Desactivar query cache (obsoleto)
query_cache_type = 0
query_cache_size = 0

# Logs (mínimos)
slow_query_log = 0

🧾 Qué hace cada cosa (explicado rápido)

🔐 Seguridad

bind-address = 127.0.0.1

👉 MariaDB solo accesible localmente (PHP + phpMyAdmin)


🧠 RAM controlada

innodb_buffer_pool_size = 64M
key_buffer_size = 16M

👉 Suficiente para:

  • Metadatos
  • Usuarios
  • Configuración
  • Tablas pequeñas

⚡ Rendimiento equilibrado

innodb_flush_log_at_trx_commit = 2

👉 Mucho menos I/O en SD
👉 Riesgo aceptable (solo perderías la última transacción en apagón)


🚦 Concurrencia realista

max_connections = 20

👉 Más que suficiente para tu uso
👉 Evita que MariaDB se coma la RAM


🔄 Guardar y reiniciar

Guarda (CTRL+O, ENTER) y sal (CTRL+X).

Luego:

sudo systemctl restart mariadb

🔎 Verificación

sudo systemctl status mariadb

Debe mostrar:

Active: active (running)

Y prueba:

mysql -u webuser -p

Si entra → optimización correcta

Reinicia:

exit;
sudo systemctl restart mariadb

Ahora tu Raspberry tiene:

  • MariaDB ligera
  • RAM bajo control
  • Seguridad local
  • Ideal para JSON + WebApps
  • phpMyAdmin estable

🔹 FASE 8 — Instalar phpMyAdmin

1️⃣ Instalar phpMyAdmin sin intentar crear la DB automática

sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt install phpmyadmin -y

Esto evita el error Access denied for user 'root'@'localhost' que da el instalador habitual.


2️⃣ Enlazar phpMyAdmin a Nginx

sudo ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin

Ahora la URL será, y el acceso con el usuario «webuser»:

http://IP_DE_LA_RASPI/phpmyadmin

3️⃣ Configuración de Nginx para phpMyAdmin

Edita tu site por defecto:

sudo nano /etc/nginx/sites-available/default

Agregamos dentro del bloque server { ... }:

server {
location /phpmyadmin {
    root /usr/share/;
    index index.php index.html index.htm;
    location ~ ^/phpmyadmin/(.+\.php)$ {
        try_files $uri =404;
        root /usr/share/;
        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
        root /usr/share/;
    }
}
}

⚠️ Cambia php8.4-fpm.sock si tu versión de PHP es distinta (ls /run/php/ para comprobar).

Reinicia Nginx:

sudo systemctl restart nginx

4️⃣ Crear base de datos (opcional) para phpMyAdmin

Si quieres usar la base tradicional phpmyadmin:

sudo mysql -u root -p

Dentro de MariaDB:

CREATE DATABASE phpmyadmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON phpmyadmin.* TO 'webuser'@'localhost' IDENTIFIED BY 'TU_PASSWORD_SEGURA';
FLUSH PRIVILEGES;
EXIT;

También puedes usar cualquier DB ya existente (apps_db) para gestión, no es obligatorio.


5️⃣ Acceder y probar

Abre en tu navegador:

http://IP_DE_LA_RASPI/phpmyadmin
  • Usuario: webuser
  • Contraseña: la que pusiste al crear webuser

Si te has olvidado de la contraseña… o la has guardado mal… así puedes cambiarla:

✔ Si entra → phpMyAdmin funcionando.

Entras como ROOT:

sudo mysql -u root

ALTER USER 'webuser'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('NUEVA_CONTRASEÑA_AQUÍ');
FLUSH PRIVILEGES;
Exit;

6️⃣ Seguridad mínima (LAN)

  • MariaDB escucha solo en 127.0.0.1 → phpMyAdmin solo accesible desde LAN
  • Usuario webuser limitado → no puede borrar root ni otras DBs
  • Opcional: añadir .htpasswd para capa extra de login

Estado tras FASE 8

  • phpMyAdmin instalado y funcional
  • MariaDB segura
  • Usuario webuser configurado
  • WebApps listas para usar DB y JSON

🔹 FASE 9 — Estructura final de WebApps y permisos

1️⃣ Carpeta principal para todas las apps

Creamos un directorio central donde se alojarán todas las WebApps:

sudo mkdir -p /var/www/apps
sudo chown -R www-data:www-data /var/www/apps
sudo chmod -R 750 /var/www/apps
  • www-data → usuario de Nginx/PHP
  • 750 → propietario tiene permisos completos, grupo puede leer/ejecutar, otros nada

2️⃣ Estructura por aplicación

Cada WebApp tendrá su propia carpeta:

/var/www/apps/
 ├── app1/
 │   ├── index.php
 │   ├── config.php       # Opcional: parámetros de DB o rutas
 │   └── data/
 │       ├── users.json
 │       ├── sessions.json
 │       └── cache/
 ├── app2/
 │   └── ...
  • data/ → solo archivos JSON
  • cache/ → opcional para datos temporales
  • index.php → archivo principal de la WebApp
  • config.php → configuración de conexión a DB o rutas locales

⚠️ Recomendación: no guardar contraseñas de root en ningún archivo PHP. Solo webuser.


3️⃣ Permisos internos por aplicación

Para cada app:

sudo mkdir -p /var/www/apps/app1/data
sudo chown -R www-data:www-data /var/www/apps/app1
sudo chmod -R 750 /var/www/apps/app1
  • Permite que PHP escriba JSON sin exponer nada a otros usuarios del sistema
  • Si varias apps necesitan acceso a la misma DB, no hay problema; solo usa webuser.

4️⃣ Configuración de conexión a MariaDB desde PHP

Ejemplo de config.php:

<?php
$DB_HOST = '127.0.0.1';
$DB_NAME = 'apps_db';
$DB_USER = 'webuser';
$DB_PASS = 'TU_PASSWORD_SEGURA';
$DB_CHARSET = 'utf8mb4';

try {
    $pdo = new PDO("mysql:host=$DB_HOST;dbname=$DB_NAME;charset=$DB_CHARSET", $DB_USER, $DB_PASS);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) {
    die("Error conectando a la DB: " . $e->getMessage());
}
  • PHP accede solo como webuser → seguridad
  • Todas las apps pueden usar este patrón

5️⃣ Estructura JSON para datos de la WebApp

Ejemplo de users.json:

[
    {
        "id": 1,
        "nombre": "SineEtiqueta",
        "email": "SineTiqueta@example.com",
        "rol": "admin"
    },
    {
        "id": 2,
        "nombre": "Usuario1",
        "email": "user1@example.com",
        "rol": "user"
    }
]

Guarda cada tipo de dato en un archivo separado para evitar JSON gigante.
Ejemplo: sessions.json, cache.json, equipos.json, etc.


6️⃣ Mejoras opcionales de seguridad

  • No exponer /data vía web
    • Opción 1: .htaccess (si usas Apache)
    • Opción 2 (mejor en Nginx):
location ~ /data/ {
    deny all;
}
  • Copia de seguridad automática de JSON y DB:
    • Scripts cron cada noche
    • Guardar en /home/pi/backups/

7️⃣ Probando la WebApp

  1. Copia tu app de AIStudio a /var/www/apps/app1/
  2. Asegúrate que index.php funciona y que puede escribir en /data
  3. Accede desde navegador:
http://IP_DE_LA_RASPI/apps/app1/
  1. Verifica:
  • Escritura/lectura JSON
  • Conexión MariaDB
  • Cache opcional si aplica

✅ Estado tras FASE 9

  • WebApps aisladas, seguras y ordenadas
  • JSON estructurado y accesible solo por PHP
  • MariaDB para metadatos, con webuser
  • Permisos ajustados para proteger datos
  • LAN lista para pruebas

💡 Tip de rendimiento:
Si vas a usar varias apps a la vez, asegúrate de no sobrepasar 4 procesos PHP simultáneos (ya lo ajustamos en la fase de PHP-FPM).

FASE INTERMEDIA – Vamos a crear un mini Cpanel para configurar aspectos de seguridad y gestión de archivos (en local)

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *