htaccess wordpress

Creando el .htaccess perfecto para WordPress

El fichero .htaccess es un archivo de configuración de Apache. Nos permite establecer reglas en un directorio, por ejemplo, WordPress lo usa para crear una estructura de enlaces amigables o permalinks.

htaccess

Además se pueden establecer otra serie de cosas como caché, compresión, restricción a ficheros y/o directorios, etc

Vamos a crear un archivo .htaccess para nuestro WordPress con las siguientes características:

  • Protección del propio archivo .htaccess
  • Protección de los archivos license.txt, readme.html y wp-config.php
  • Desactivar la navegación por directorios
  • Desactivar la firma del servidor
  • Seguridad contra hacking e Injections
  • Caché de archivos
  • Compresión Gzip
  • Enlaces amigables

Nuestro archivo .htaccess optimizado para WordPress quedaría de la siguiente manera:


# BEGIN WordPress
# Las directivas (líneas) entre `BEGIN WordPress` y `END WordPress` se generan dinámicamente
# , y solo se deberían modificar mediante filtros de WordPress.
# Cualquier cambio en las directivas que hay entre esos marcadores se sobreescribirán.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

# BEGIN DWP WPO
# Las directivas (líneas) entre `BEGIN DWP WPO` y `END DWP WPO` se generan dinámicamente
# , y solo se deberían modificar mediante filtros de WordPress.
# Cualquier cambio en las directivas que hay entre esos marcadores se sobreescribirán.
# LEVERAGE BROWSER CACHING
<IfModule mod_mime.c>
  AddType application/font-woff2 .woff2
  AddType application/x-font-woff2 .woff2
</IfModule>

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access 1 month"
# HTML
  ExpiresByType text/html A3600
# CSS
  ExpiresByType text/css A31536000
# JavaScript
  ExpiresByType application/javascript A31536000
  ExpiresByType text/javascript A31536000
# HTML components (HTCs)
  ExpiresByType text/x-component A31536000
  ExpiresByType text/x-js A31536000
  ExpiresByType application/x-javascript A31536000
# Data interchange
  ExpiresByType application/json A31536000
  ExpiresByType application/xml A3600
  ExpiresByType text/xml A3600
# Manifest files
  ExpiresByType application/x-web-app-manifest+json A3600
  ExpiresByType text/cache-manifest A3600
# Web feeds
  ExpiresByType application/atom+xml A3600
  ExpiresByType application/rss+xml A3600
# Favicon (cannot be renamed!)
  ExpiresByType image/x-icon A31536000
# Images
  ExpiresByType image/svg+xml A31536000
  ExpiresByType image/bmp A31536000
  ExpiresByType image/gif A31536000
  ExpiresByType image/jpg A31536000
  ExpiresByType image/jpeg A31536000
  ExpiresByType image/webp A31536000
  ExpiresByType image/png A31536000
# Media
  ExpiresByType audio/mpeg A31536000
  ExpiresByType audio/ogg A31536000
  ExpiresByType audio/wma A31536000
  ExpiresByType video/asf A31536000
  ExpiresByType video/quicktime A31536000
  ExpiresByType video/mp4 A31536000
  ExpiresByType video/mpeg A31536000
  ExpiresByType video/ogg A31536000
  ExpiresByType video/webm A31536000
# Web fonts
  ExpiresByType application/font-woff A31536000
  ExpiresByType application/font-woff2 A31536000
  ExpiresByType application/x-font-woff A31536000
  ExpiresByType application/vnd.ms-fontobject A31536000
  ExpiresByType application/x-font-otf A31536000
  ExpiresByType application/x-font-ttf A31536000
  ExpiresByType font/opentype A31536000
  ExpiresByType image/svg+xml A31536000
</IfModule>

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE image/bmp
  AddOutputFilterByType DEFLATE image/x-icon
  AddOutputFilterByType DEFLATE image/webp
  AddOutputFilterByType DEFLATE image/vnd.microsoft.icon
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/richtext
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/cache-manifest
  AddOutputFilterByType DEFLATE text/vcard
  AddOutputFilterByType DEFLATE application/json
  AddOutputFilterByType DEFLATE application/ld+json
  AddOutputFilterByType DEFLATE application/manifest+json
  AddOutputFilterByType DEFLATE application/schema+json
  AddOutputFilterByType DEFLATE application/x-web-app-manifest+json
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/rdf+xml
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/pdf
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE application/x-font
  AddOutputFilterByType DEFLATE application/x-font-opentype
  AddOutputFilterByType DEFLATE application/x-font-truetype
  AddOutputFilterByType DEFLATE application/x-font-otf
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/x-font-woff
  AddOutputFilterByType DEFLATE application/font-woff
  AddOutputFilterByType DEFLATE application/font-woff2
  AddOutputFilterByType DEFLATE font/opentype
  AddOutputFilterByType DEFLATE font/ttf
  AddOutputFilterByType DEFLATE font/eot
  AddOutputFilterByType DEFLATE font/otf
</IfModule>

<IfModule mod_headers.c>
  Header unset ETag
  FileETag None
</IfModule>

<FilesMatch "\.(css|js|html|htm|rtf|rtx|svg|txt|xsd|xsl|xml|asf|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|webp|json|mdb|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|_otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|swf|tar|tif|tiff|ttf|ttc|_ttf|wav|wma|woff|woff2|xla|xls|xlsx|xlt|xlw|zip)$">
  <IfModule mod_headers.c>
    Header set Pragma "public"
    Header append Cache-Control "public"
    Header set Expires "max-age=31536000, public"
  </IfModule>
</FilesMatch>
# END DWP WPO
# BEGIN DWP SECURITY
# Las directivas (líneas) entre `BEGIN DWP SECURITY` y `END DWP SECURITY` se generan dinámicamente
# , y solo se deberían modificar mediante filtros de WordPress.
# Cualquier cambio en las directivas que hay entre esos marcadores se sobreescribirán.

# HTTP SECURITY HEADERS
<IfModule mod_headers.c>
  Header always append X-Frame-Options SAMEORIGIN
  Header set X-Content-Type-Options nosniff
  Header set X-XSS-Protection "1; mode=block"
  Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>

# DISABLE DIRECTORY BROWSING
Options All -Indexes

# DISABLE SERVER SIGNATURE
ServerSignature Off

# STRONG IMPORTANT FILES PROTECTION
<FilesMatch "^.*(error_log|license\.txt|readme\.html|wp-config\.php|\.[hH][tT][aApP].*)$">
  Require all denied
</FilesMatch>

# STRONG IMPORTANT FILES PROTECTION II: IN WP-ADMIN
<Files ~ "^(install|setup\-config)\.php$">
  Require all denied
</Files>

# PREVENT CODE HACKING AND INJECTIONS
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{QUERY_STRING} proc/self/environ [OR]
  RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|\%3D) [OR]
  RewriteCond %{QUERY_STRING} base64_encode.*(.*) [OR]
  RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
  RewriteCond %{QUERY_STRING} GLOBALS(=|[|\%[0-9A-Z]{0,2}) [OR]
  RewriteCond %{QUERY_STRING} _REQUEST(=|[|\%[0-9A-Z]{0,2})
  RewriteRule ^(.*)$ index.php [F,L]
</IfModule>

# STOPS BOTS TRYING TO REGISTER IN WORDPRESS SITES THAT HAVE REGISTRATION DISABLED
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{QUERY_STRING} ^action=register$ [NC,OR]
  RewriteCond %{HTTP_REFERER} ^.*registration=disabled$ [NC]
  RewriteRule (.*) - [F]
</IfModule>

# BLOCK TRACE & TRACK REQUEST METHODS
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)$
  RewriteRule (.*) - [F]
</IfModule>

# BLOCK ANY QUERY STRING TRYING TO GET A COPY OF WP-CONFIG.PHP FILE
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{QUERY_STRING} ^.*=(.*wp-config\.php)$ [NC]
  RewriteRule (.*) - [F]
</IfModule>

# BLOCK WPSCAN BY USER-AGENT
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_USER_AGENT} WPScan
  RewriteRule (.*) http://127.0.0.1 [L,R=301]
</IfModule>

# HOT LINKING PROTECTION
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_REFERER} !^$
  RewriteCond %{HTTP_REFERER} !^https://desarrollowp.com.*$ [NC]
  RewriteRule \.(jpg|jpeg|png|gif|bmp|zip|rar|css|pdf)$ http://i.imgur.com/MlQAH71.jpg [NC,F,L]
</IfModule>
# END DWP SECURITY

¿Te ha resultado útil esta información? 🍺

Si este post te ha resuelto un problema, invítame a un café o a una cerveza. Con este pequeño gesto me animas a seguir escribiendo.

Comentarios

11 comentarios en Creando el .htaccess perfecto para WordPress

  1. Hola Pablo, tienes un blog muy interesante y que al menos a mi me esta ayudando y enseñando mucho.

    Se puede decir que este archivo .htaccess es un estandar para la mayoria de las instalaciones WordPress???

    1. Hola Juanjo

      Gracias por tus palabras. La verdad es que no hay un htaccess «perfecto» o «estandar». Cierto es que muchas cosas son muy genéricas, y le pueden servir a cualquiera, pero al final cada proyecto, cada necesidad o cada web es un mundo

  2. ¡Muchas gracias, Pablo! Uno de mis sitios daba error 403 y la ayuda del hosting fue muy pobre. Gracias a ti, ya pude solucionarlo. Que disfrutes tu café!

  3. Hola, Pablo. Muy interesante. Hay forma de solucionar un conflicto de SSL que genera un conflicto «cURL error 35: parece que estas desconetado» desde .htaccess ?

    Es como que no toma todo en https y para peor… desde otros dispositivos (celular u otra pc) no me permite modificar entradas, subir fotos…esta como bloqueado.

    Sospecho que el tema esta acá.

    Agradecería tu ayuda.

    Excelente blog

    1. No estoy seguro de que resuelvas el error mediante alguna regla en el .htaccess, tiene pinta de que tiene algo que ver con el certificado de seguridad de tu sitio. Te recomendaría que abrieras un ticket con tu proveedor de hosting

  4. Hola exelente informacion , Sinceramente intente probar esto en mis hosting con almalinux y version php 8.0 y realmente ninguna de estas reglas tuvo efecto.

    Sabes si esto se debe a que uso almalinux o la version de php no es la correcta?

    1. Hola. Puede ser que tu hosting tenga alguna limitación al respecto, lo aconsejable es que te pongas en contacto con su soporte técnico y que te saquen de dudas.

  5. Hola Pablo, Espero que estes muy bien, consulta, sabes que la App de WooCommerce no me funciona, y sospecho que es la restriccion que puso mi hosting, en el archivo .htacces existe algun codigo que me permita solo darle acceso puntual a jetpack y WooCommerce? tengo entendido que si uno bloquea por completo el acceso es mas seguro pero limita el uso de las app¿?

    1. Hola. Puede ser que tu hosting tenga alguna limitación al respecto, lo aconsejable es que te pongas en contacto con su soporte técnico y que te saquen de dudas.

Deja un comentario

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

  • El fin del tratamiento es únicamente la moderación de comentarios para evitar spam
  • La legitimación es tu consentimiento al comentar
  • No se comunicará ningún dato a terceros salvo por obligación legal
  • Tienes derecho al acceso, rectificación y eliminación de los comentarios