Esta guía está pensada para quien instala o personaliza Risus Nova (o cualquier CMS en PHP) y quiere entender de verdad qué hace su `.htaccess`, en vez de copiar y pegar sin saber por qué.---
## 1. ¿Qué es el .htaccess?
`.htaccess` (de "hypertext access") es un archivo de configuración del servidor web **Apache**. Se coloca dentro de una carpeta, y sus reglas se aplican a esa carpeta y a todas las que tenga dentro.
Su gran ventaja: te permite cambiar el comportamiento del servidor **sin tener acceso a la configuración global de Apache** — algo imprescindible en hosting compartido, donde nunca vas a poder tocar `httpd.conf`.
### Requisito importante
Para que Apache respete un `.htaccess`, la configuración global del servidor tiene que tener activado:
```apache
AllowOverride All
```
En el 99% de los hostings compartidos (incluidos los gratuitos) esto ya viene activado por defecto.
Si tu `.htaccess` "no hace nada" y no ves ningún error, este suele ser el motivo — y normalmente no puedes solucionarlo tú mismo, hay que pedírselo al hosting.
---
## 1.1 Requisitos previos: qué necesita tu servidor para que funcione el .htaccess de Risus Nova
Antes de subir el `.htaccess`, el servidor necesita tener activos estos módulos de Apache.
Sin ellos, por muy bien escrito que esté el archivo, las URLs amigables (`/posts/categoria/`) no van a funcionar y te van a salir 404 en todo el sitio.
### Lo que hace falta, en concreto
| Requisito | Para qué sirve |
|---|---|
| `mod_rewrite` activado | Sin esto, `RewriteEngine On` no hace absolutamente nada |
| `AllowOverride All` (o al menos `FileInfo Indexes Limit`) | Permite que el `.htaccess` pueda sobreescribir la configuración del servidor |
| `mod_headers` (opcional, recomendado) | Para las cabeceras de seguridad (`X-Frame-Options`, etc.) |
| PHP 8.0 o superior | Requisito del propio script, no de Apache |
### En hosting compartido (cPanel, Hostinger, etc.)
Normalmente **ya viene todo activado de fábrica** — no tienes que hacer nada.
Si aun así las URLs no funcionan:
1. Entra a cPanel/hPanel → busca **"Seleccionar versión de PHP"** y confirma que tienes PHP 8.x.
2. Si tienes acceso a **"MultiPHP INI Editor"** o similar, revisa que no haya nada bloqueando `mod_rewrite`.
3. Si nada de esto soluciona el problema, escríbele a soporte técnico de tu hosting y pregúntales literalmente: *"¿Tengo mod_rewrite activado y AllowOverride All en mi cuenta?"* — es una pregunta de 30 segundos para ellos.
### En un VPS o servidor propio (acceso root)
Si administras tú mismo el servidor, tienes que activarlo a mano:
```bash
# Activar el módulo (Debian/Ubuntu)
sudo a2enmod rewrite
sudo a2enmod headers
sudo systemctl restart apache2
```
Y en el archivo de configuración del sitio (normalmente en `/etc/apache2/sites-available/tu-sitio.conf`), asegúrate de tener:
```apache
<Directory /var/www/tu-sitio/public_html>
AllowOverride All
Require all granted
</Directory>
```
Sin el `AllowOverride All` ahí dentro, tu `.htaccess` se ignora por completo, aunque `mod_rewrite` esté activado.
### En local, con XAMPP (para hacer pruebas en tu PC)
Esto es justo lo que mucha gente se encuentra al instalar Risus Nova en local para probarlo antes de subirlo:
1. Abre `xampp/apache/conf/httpd.conf` con un editor de texto.
2. Busca la línea `LoadModule rewrite_module modules/mod_rewrite.so` y quita el `#` de delante si lo tiene (si tiene `#` está desactivado).
3. Busca el bloque `<Directory "C:/xampp/htdocs">` y cambia `AllowOverride None` por `AllowOverride All`.
4. Guarda, y reinicia Apache desde el Panel de Control de XAMPP.
Si después de esto sigues sin ver las URLs amigables, reinicia Apache otra vez — a veces XAMPP necesita un reinicio completo (no solo "Stop/Start") para que el cambio de `httpd.conf` se aplique.
### En hosting gratuito (000webhost, InfinityFree, Byethost...)
La buena noticia: los hostings gratuitos serios suelen tener `mod_rewrite` y `AllowOverride All` activados de fábrica, porque sin eso no podrían alojar ni WordPress ni casi ningún CMS popular — así que en la mayoría de los casos **no tienes que activar nada a mano**, simplemente subes tu `.htaccess` y funciona.
Donde sí suelen tener limitaciones específicas (y esto varía bastante de un proveedor a otro, así que tómalo como una lista de sospechosos habituales, no como una garantía):
- **Algunos bloquean `php_value`/`php_flag` en el `.htaccess`** por seguridad, y obligan a configurar PHP desde su propio panel en su lugar. Si tu `.htaccess` te da un error 500 justo al añadir el bloque de `php_value`, prueba a quitar esa parte — el resto (las reglas de URLs) debería seguir funcionando igual.
- **Algunos limitan qué módulos puedes usar dentro de `<IfModule>`**, o no tienen `mod_headers` activado — si las cabeceras de seguridad no parecen aplicarse, no es necesariamente un fallo tuyo.
- **El panel de control suele ser más simple** (sin las opciones "MultiPHP" de cPanel) — para confirmar la versión de PHP, busca normalmente una sección llamada algo como "PHP Settings" o "Configuración de PHP" en su panel.
Si tras subir el `.htaccess` de la plantilla de este tutorial te sigue sin funcionar nada en un hosting gratuito, lo más rápido es ir quitando bloques uno a uno (primero el de `php_value`, luego el de `mod_headers`) hasta encontrar cuál es el que el proveedor no permite, en vez de asumir que el archivo entero está mal.
---
## 2. ¿Cómo se "lee"? Orden y cascada
Apache procesa las reglas **de arriba a abajo**, y se detiene en la primera regla que coincida y lleve la bandera `[L]` (Last / última). Esto es clave: el orden de las reglas importa, y poner algo demasiado genérico arriba puede "tapar" reglas más específicas que pongas después.
También puede haber varios `.htaccess` anidados (uno en la raíz, otro en una subcarpeta) — el de la subcarpeta hereda y puede sobreescribir al de la carpeta padre.
---
## 3. Las partes de un .htaccess (explicadas una a una)
### 3.1 Páginas de error personalizadas
```apache
ErrorDocument 404 /error404.html
ErrorDocument 403 /error403.html
ErrorDocument 401 /error401.html
```
Le dice a Apache qué página mostrar cuando ocurre ese código de error, en vez de la página fea por defecto del servidor.
### 3.2 mod_rewrite: el corazón de las URLs amigables
Esto es lo que convierte `index.php?do=posts&cat=peliculas` en algo limpio como `/posts/peliculas/`.
```apache
RewriteEngine On
RewriteRule ^posts/([A-Za-z0-9_-]+)/$ index.php?do=posts&cat=$1 [QSA,L]
```
Cómo leerlo:
- `RewriteEngine On` → activa el motor de reescritura. Va siempre primero, una sola vez.
- `RewriteRule patrón destino [flags]` → si la URL solicitada coincide con el patrón (una expresión regular), Apache la redirige internamente al destino.
- `([A-Za-z0-9_-]+)` → un grupo de captura. Lo que el usuario escriba ahí se guarda en `$1`, `$2`, etc., en el orden en que aparecen los paréntesis.
Los flags más usados:
| Flag | Significado |
|---|---|
| `[L]` | *Last*. Para aquí, no sigas comprobando más reglas. |
| `[QSA]` | *Query String Append*. Conserva los parámetros `?algo=valor` que ya traía la URL, en vez de borrarlos. |
| `[NC]` | *No Case*. Ignora mayúsculas/minúsculas al comparar. |
| `[OR]` | Une esta condición con la siguiente con un "O" lógico (por defecto es "Y"). |
| `[F]` | *Forbidden*. Devuelve un 403 y corta la petición ahí mismo. |
| `[R]` o `[R=301]` | Redirección visible al navegador (cambia la URL que ve el usuario), en vez de interna. |
### 3.3 RewriteCond: condiciones antes de la regla
`RewriteCond` se pone **antes** de un `RewriteRule` y añade una condición que debe cumplirse para que esa regla se aplique.
```apache
RewriteCond %{HTTP_USER_AGENT} ^$ [OR]
RewriteCond %{HTTP_USER_AGENT} (libwww-perl|nikto|clshttp) [NC]
RewriteRule .* - [F,L]
```
Esto dice: "si el navegante no manda User-Agent, **o** si su User-Agent contiene alguna de esas palabras (típicas de bots maliciosos), bloquea cualquier URL (`.*`) con un 403".
⚠️ Error muy común (y que se ve mucho en `.htaccess` copiados de internet sin entenderlos): poner un montón de `RewriteCond` encadenadas y **olvidar la `RewriteRule .* - [F,L]` final**. Sin esa línea, las condiciones no bloquean absolutamente nada — solo están ahí decorativas. Si tu "protección antibots" no parece estar haciendo nada, esto es lo primero que hay que revisar.
### 3.4 Options: comportamiento de la carpeta
```apache
Options +FollowSymLinks
Options All -Indexes
```
- `+FollowSymLinks` → necesario casi siempre para que `mod_rewrite` funcione correctamente.
- `-Indexes` → evita que, si entras a una carpeta sin `index.php`, Apache te muestre un listado de todos los archivos que hay dentro. Sin esto, cualquiera podría listar el contenido de tus carpetas de subida, por ejemplo.
### 3.5 Cabeceras de seguridad (mod_headers)
```apache
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
</IfModule>
```
Envuelve esto siempre en `<IfModule mod_headers.c>` — si el hosting no tiene ese módulo activado, esto evita un error fatal de configuración en vez de simplemente no funcionar.
- `X-Content-Type-Options: nosniff` → impide que el navegador intente "adivinar" el tipo de un archivo, lo que reduce ciertos ataques XSS.
- `X-Frame-Options: SAMEORIGIN` → evita que tu web se pueda meter dentro de un `<iframe>` en otro dominio (protección contra clickjacking).
- `X-XSS-Protection` → filtro XSS heredado de navegadores antiguos. Ya casi no hace nada en navegadores modernos, pero no molesta dejarlo.
### 3.6 Proteger archivos sensibles
```apache
<FilesMatch "\.(htaccess|ini|log|cfg|sql|bak|env)$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order Allow,Deny
Deny from all
</IfModule>
</FilesMatch>
```
Esto impide que alguien pueda descargar directamente archivos de configuración, copias de seguridad de la base de datos, logs, etc., simplemente escribiendo su ruta en el navegador.
⚠️ Importante — Apache 2.2 vs Apache 2.4: `Order Allow,Deny` / `Deny from all` es la sintaxis **antigua** (Apache 2.2). `Require all denied` es la sintaxis **moderna** (Apache 2.4). Si tu hosting es Apache 2.4 "puro" (sin el módulo de compatibilidad `mod_access_compat`), la sintaxis antigua **se ignora en silencio y no protege nada**. Por eso, lo más seguro es poner las dos, cada una dentro de su `<IfModule>`, como en el ejemplo de arriba — así funciona sin importar qué versión tenga el hosting.
### 3.7 Configuración de PHP desde el .htaccess
```apache
<IfModule php8_module>
php_value post_max_size 8M
php_value upload_max_filesize 2M
php_value max_input_vars 1000
php_flag display_errors Off
</IfModule>
```
Permite ajustar valores de PHP sin tocar el `php.ini` del servidor (que en hosting compartido normalmente no puedes editar). El nombre del `<IfModule>` cambia según cómo tenga el hosting configurado PHP (`php8_module`, `lsapi_module`, etc.) — si no sabes cuál usa tu hosting, puedes poner varios bloques, uno para cada posibilidad, y simplemente el que no aplique se ignora.
---
## 4. Errores típicos que hay que evitar
1. **Bloquear sin querer a tu propia API o a un cron.** Si filtras por `User-Agent` para bloquear bots maliciosos, asegúrate de no estar bloqueando `curl`/`wget`/`python`, que son herramientas legítimas para consumir una API o ejecutar tareas programadas.
2. **Reglas sin `[L]`.** Si una `RewriteRule` no lleva `[L]` y coincide, Apache sigue probando las siguientes reglas con el resultado ya modificado — puede llevar a resultados inesperados si no es lo que querías.
3. **Mezclar sintaxis de Apache 2.2 y 2.4 sin los `<IfModule>` de respaldo.**
4. **Expresiones regulares mal cerradas o corruptas.** Una regla como `^moderacion/buscador/([0-2]+)/([0-2]+)/([^/]+)$` es mucho más mantenible y segura que intentar definir manualmente "todos los caracteres permitidos" carácter por carácter — usa clases como `[^/]+` (cualquier cosa que no sea una barra) cuando puedas, en vez de listas interminables.
5. **No probar después de cada cambio.** Un solo carácter mal puesto en una regex puede tirar la web entera (error 500). Cambia una cosa, prueba, y si funciona, sigue con la siguiente.
---
## 5. Plantilla base recomendada (para copiar y adaptar)
```apache
# Páginas de error personalizadas
ErrorDocument 404 /error404.html
ErrorDocument 403 /error403.html
RewriteEngine On
Options +FollowSymLinks
Options All -Indexes
# Cabeceras de seguridad
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
</IfModule>
# Bloqueo de bots/escaneos maliciosos (con la regla final que de verdad bloquea)
RewriteCond %{HTTP_USER_AGENT} ^$ [OR]
RewriteCond %{HTTP_USER_AGENT} (libwww-perl|nikto|clshttp|sqlmap|acunetix) [NC]
RewriteRule .* - [F,L]
# --- Aquí van tus reglas de URLs amigables, una por una ---
RewriteRule ^posts/([A-Za-z0-9_-]+)/$ index.php?do=posts&cat=$1 [QSA,L]
# Protección de archivos sensibles
<FilesMatch "\.(htaccess|ini|log|sql|bak|env)$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order Allow,Deny
Deny from all
</IfModule>
</FilesMatch>
```
---
## 6. Cómo depurar cuando algo no funciona
- **Error 500 al guardar el `.htaccess`** → casi siempre es una `RewriteRule` con una regex mal escrita, o un módulo (`<IfModule>`) que no existe en tu hosting y no lo envolviste bien.
- **La regla "no hace nada"** → revisa que no haya otra regla *antes* con `[L]` que esté capturando la petición primero.
- **Quieres probar una expresión regular antes de subirla** → puedes usar cualquier "tester" de regex online (buscando "regex tester") pegando tu patrón y una URL de ejemplo, para confirmar que coincide como esperas, antes de tocar el servidor real.
- **Revisa el log de errores de Apache** (en hosting compartido normalmente desde el panel de control; en local, en XAMPP está en `xampp/apache/logs/error.log`) — ahí Apache suele explicar exactamente qué línea le da problemas.
---
## 7. Extras de seguridad y rendimiento que vale la pena añadir
Estos bloques no son obligatorios, pero en un sitio en producción de verdad marcan la diferencia. Puedes añadirlos todos a la vez o ir probando uno por uno.
### 7.1 Forzar HTTPS
Si tu hosting tiene SSL (la mayoría lo dan gratis hoy en día con Let's Encrypt), esto obliga a que **toda** la web se vea siempre con `https://`, aunque alguien escriba `http://` o un enlace viejo apunte sin el "s":
```apache
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
```
Ponlo **antes** que el resto de tus reglas de URLs amigables.
### 7.2 Forzar "www" o quitarlo (evita contenido duplicado)
Para Google, `tusitio.com` y `www.tusitio.com` son dos sitios distintos si no rediriges uno al otro — eso reparte tu SEO entre dos URLs en vez de concentrarlo en una. Elige una de las dos opciones (no las dos):
Quitar el www (lo más habitual hoy en día):
```apache
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
```
Forzar el www:
```apache
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ Registrate o inicia tu sesión para ver este contenido%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
```
### 7.3 Impedir que se ejecute PHP dentro de las carpetas de subida (muy recomendado)
Esto es importante para cualquier script que acepte subida de archivos — como la imagen de portada o los avatares de Risus Nova. Aunque el código ya valida que el archivo subido sea una imagen real, esta regla añade una capa extra: **incluso si alguien consiguiera subir un archivo `.php` disfrazado**, el servidor nunca lo ejecutaría como código, solo lo serviría como archivo plano e inofensivo.
Crea un `.htaccess` **distinto y nuevo**, dentro de la propia carpeta de subidas (`files/uploads/.htaccess`), con solo esto:
```apache
<FilesMatch "\.(php|php[0-9]|phtml|pl|py|cgi|asp|sh)$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order Allow,Deny
Deny from all
</IfModule>
</FilesMatch>
```
### 7.4 Compresión GZIP (la página carga más rápido)
```apache
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json
</IfModule>
```
Comprime el HTML/CSS/JS antes de enviarlo al navegador — páginas más ligeras, carga más rápida, sobre todo notable en conexiones móviles.
### 7.5 Cache del navegador para imágenes y estáticos
```apache
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 1 week"
</IfModule>
```
Le dice al navegador "esta imagen no va a cambiar en un mes, no hace falta que la vuelvas a descargar" — en visitas repetidas, la web carga casi al instante.
### 7.6 Evitar el "hotlinking" de tus imágenes
Si no quieres que otras webs enlacen directamente a tus imágenes (consumiendo tu ancho de banda sin que el visitante entre a tu sitio):
```apache
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https://(www\.)?tudominio\.com/.*$ [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp)$ - [F,L]
```
Cambia `tudominio.com` por el tuyo. Esto permite que las imágenes se vean en tu propia web con normalidad, pero si otra página intenta mostrarlas directamente, le sale un error.
### 7.7 Desactivar ETags (evita una pequeña fuga de información)
```apache
FileETag None
```
Por defecto, Apache manda una cabecera `ETag` que puede filtrar detalles internos del servidor (inode, tamaño exacto, fecha). Quitarla no rompe nada y reduce un poco la "huella" que da tu servidor.
### 7.8 Limitar qué métodos HTTP se aceptan
La mayoría de los sitios solo necesitan `GET`, `POST` y `HEAD`. Bloquear el resto (como `TRACE`, usado en algunos ataques antiguos) es gratis:
```apache
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
```


Facebook
Twitter
Reddit
Digg
del.icio.us
Tumblr
Pinterest
Blogger
Fark
LinkedIn
Mix
Google