En servidores Apache es posible utilizar .htaccess y mod_rewrite para acceder a archivos PHP o HTML mediante URLs sin extensión. Por ejemplo, el visitante abre /contacto y Apache carga internamente contacto.php sin mostrar el nombre físico en la barra del navegador.
Esta técnica puede simplificar las URLs de un sitio desarrollado con archivos tradicionales, pero requiere planificar redirecciones, enlaces internos, formularios y posibles colisiones. No mejora la seguridad por sí sola y no suele ser necesaria en WordPress u otros CMS que ya administran sus rutas.
Reescritura interna y redirección no son lo mismo
Una reescritura interna cambia el archivo que Apache procesa sin modificar la URL visible. Una redirección externa devuelve un código 301 o 302 y hace que el navegador solicite otra dirección.
Para una migración completa suelen utilizarse ambas:
- La URL antigua con extensión redirige hacia la versión limpia.
- La versión limpia se reescribe internamente al archivo existente.
Si sólo implementás el segundo paso, /contacto funcionará, pero /contacto.php también seguirá accesible. Eso deja dos URLs para el mismo contenido.
Requisitos y precauciones
- Servidor Apache con
mod_rewritehabilitado. - Permiso para utilizar reglas de reescritura en
.htaccess. - Copia del archivo actual antes de modificarlo.
- Acceso al registro de errores o al Administrador de archivos para revertir cambios.
- Inventario de las URLs existentes que reciben visitas o enlaces.
La documentación oficial de .htaccess de Apache explica que las reglas se aplican al directorio donde está el archivo y a sus subdirectorios. En este contexto, el patrón de RewriteRule no comienza con una barra.
Acceder a archivos PHP sin mostrar .php
En el .htaccess de la carpeta donde están los archivos, podés utilizar:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
Las condiciones evitan reescribir directorios reales y comprueban que exista un archivo PHP con el mismo nombre. Si se solicita /contacto y existe contacto.php, Apache lo procesa internamente.
Esta regla no convierte automáticamente enlaces. Debés actualizar menús, botones, mapas del sitio y referencias internas para apuntar a la URL elegida.
Acceder a archivos HTML sin mostrar .html
Para archivos estáticos HTML, la estructura equivalente es:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+?)/?$ $1.html [L]
Así, /nosotros puede cargar nosotros.html. Verificá los enlaces relativos dentro del archivo: recursos como imagenes/logo.png pueden resolverse de manera diferente según la profundidad de la URL. Las rutas desde la raíz, como /imagenes/logo.png, suelen ser más previsibles.
Combinar PHP y HTML
Si el sitio contiene ambos formatos, podés probar primero PHP y luego HTML:
RewriteEngine On
# PHP
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
# HTML
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+?)/?$ $1.html [L]
El orden define la prioridad. Si existen ayuda.php y ayuda.html, la solicitud /ayuda cargará la versión PHP. Evitá nombres duplicados o documentá cuál debe prevalecer.
Redirigir las URLs antiguas con extensión
Una vez comprobada la reescritura interna, podés redirigir solicitudes directas de .php hacia la URL limpia:
RewriteCond %{THE_REQUEST} \s/+(.+?)\.php(?:[\s?]) [NC]
RewriteRule ^(.+)\.php$ /$1 [R=301,L]
Para HTML:
RewriteCond %{THE_REQUEST} \s/+(.+?)\.html(?:[\s?]) [NC]
RewriteRule ^(.+)\.html$ /$1 [R=301,L]
THE_REQUEST representa la línea solicitada originalmente por el navegador. Permite diferenciar una petición visible a archivo.php de la reescritura interna generada al cargar /archivo, evitando un bucle.
La guía de redirecciones y remapeos de Apache distingue las reescrituras internas de las redirecciones externas y muestra cómo verificar que el archivo de destino exista.
Ejemplo completo para PHP
RewriteEngine On
# Redirigir /archivo.php hacia /archivo
RewriteCond %{THE_REQUEST} \s/+(.+?)\.php(?:[\s?]) [NC]
RewriteRule ^(.+)\.php$ /$1 [R=301,L]
# Cargar archivo.php al solicitar /archivo
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
Probá primero con una sola página. Cuando confirmes que conserva parámetros, formularios y recursos, aplicalo al resto del sitio.
Qué ocurre con parámetros y formularios
Una URL como /buscar.php?q=hosting debe terminar en /buscar?q=hosting. Verificá que la cadena de consulta se conserve. Apache normalmente mantiene los parámetros cuando el destino no define una nueva cadena, pero otras reglas pueden alterarlos.
Revisá también el atributo action de los formularios, llamadas AJAX, enlaces generados por PHP, URLs incluidas en correos y cualquier integración externa. Una página que abre correctamente puede seguir enviando datos a la dirección antigua.
Sitios alojados en una subcarpeta
Si el sitio funciona en https://ejemplo.com/app/, no copies reglas pensadas para la raíz sin probarlas. El contexto de directorio cambia el patrón y el destino de la redirección. Puede ser necesario ajustar la ruta de sustitución o utilizar RewriteBase según la estructura.
Una redirección que comienza con /$1 siempre apunta a la raíz del dominio. En una subcarpeta podría enviar al visitante fuera de la aplicación.
WordPress y otros CMS
WordPress, Joomla, Drupal, PrestaShop y muchos frameworks utilizan un controlador frontal y reglas propias. Sus URLs ya pueden no corresponder a archivos físicos. Agregar este bloque por encima o dentro de sus reglas puede interceptar solicitudes, romper el panel o generar respuestas incorrectas.
En WordPress no modifiques el bloque situado entre # BEGIN WordPress y # END WordPress. Si las reglas normales se dañaron, restaurá el .htaccess predeterminado de WordPress en lugar de agregar reglas para extensiones.
Impacto en SEO y URLs canónicas
Ocultar la extensión no produce por sí mismo una mejora de posicionamiento. El beneficio principal es mantener URLs estables aunque cambie la tecnología interna. Para una migración ordenada:
- elegí una única versión canónica;
- utilizá redirecciones 301 desde las URLs antiguas;
- actualizá enlaces internos, canonical y sitemap;
- conservá la ruta y los parámetros necesarios;
- evitá cadenas de varias redirecciones.
No cambies simultáneamente protocolo, dominio, estructura de carpetas y extensiones sin un mapa de redirecciones. Separar las etapas facilita detectar fallas.
No es una medida de seguridad
Que la URL no muestre .php no oculta de forma confiable la tecnología ni corrige vulnerabilidades. Cabeceras, cookies, errores y comportamiento de la aplicación pueden revelar información. La seguridad depende de código actualizado, validación de entradas, permisos, configuraciones correctas y protección del servidor.
Tampoco permite bloquear la descarga de copias como archivo.php.bak. Para ese objetivo consultá cómo bloquear el acceso web a archivos sensibles con .htaccess y, preferentemente, guardá los respaldos fuera de public_html.
Pruebas antes de publicar
- Guardá una copia del
.htaccess. - Probá una URL limpia y su versión antigua.
- Confirmá que la antigua responda 301 y llegue en un solo salto.
- Verificá parámetros, formularios, imágenes, CSS y JavaScript.
- Probá una URL inexistente y comprobá que siga devolviendo 404.
- Revisá los registros y Search Console después de publicar.
Qué hacer si aparece un error 500
Restaurá inmediatamente la copia anterior. Un 500 suele indicar sintaxis inválida, una directiva no permitida o una expresión incompatible. Consultá el registro de errores antes de probar variaciones al azar.
Las URLs sin extensión funcionan bien cuando se diseñan como una migración completa: reescritura interna, redirección canónica, actualización de enlaces y pruebas. Utilizar sólo una regla aislada puede dejar contenido duplicado o romper rutas existentes.