Convertir una web con wordpress a estática con httrack

Hace unos días llegó a mí una de aquellas tareas que en teoría tenía que ser poquitas horas, las poquitas horas han terminado siendo casi un mes entero de trabajo.

La tarea encomendada era la de arreglar la portada de la página web del periódico digital de contrainformación kaosenlared.net.

La web estaba usando el tema publisher y la portada estaba construida con gutenberg, además para cachear la web se estaba usando el plugin W3 total cache. Una combinación del tema y los dos plugins mencionados hacían que se descuadrase toda la página. Por suerte se disponía de una copia de seguridad de un día anterior a que se rompiese la portada. Mi tarea fue pues reestablecer la configuración del tema desde las opciones de exportación y cargar el contenido de la página de la portada. Al arreglarlo y volver a poner la página en producción, a las pocas horas, la portada se volvía a descuadrar. El cliente pidió simplemente dar de baja esta web, hacer algo con los archivos y aprovechar para crear otra nueva que ya estaban usando como web de emergencia.

Así que mi propuesta fue arreglar de nuevo el wordpress y convertir la página web a estática, ya que la página web tenía un pequeño detallito de nada, la friolera de 407.000 entradas, cosa que hacía que el consumo de recursos de la web fuese espectacular, por la gran cantidad de páginas y usuarios que visitaban diariamente el sitio.

Lo primero pues fue copiar todo el contenido del blog antiguo (67Gb + 8Gb de BBDD) a mi humilde infraestructura bajo otro subdominio y empezar con la tarea de convertir el sitio a estático. Me estuve mirando varios plugins pero por una web de tal tamaño no me quedó otra que usar httrack.

Empiezo a usar httrack sobre la web montada sobre un apache simple y llano, tal como estaba en el servidor en producción y aún así la portada a las pocas horas se volvía a descuadrar. La solución fue desactivar el plugin W3 Total Cache y activar varnish, ya que el crawling de la web se esperaba intenso.

Monté el wordpress finalmente a mi manera, con apache para servir la web internamente para servirla finalmente con nginx

Apache

root@test-wp-kaos:/etc/apache2/sites-enabled# cat kaos2.conf 
###############################################
# Capa8
###############################################
<VirtualHost *:8080>
        ServerAdmin admin@capa8.net

        DocumentRoot /home/virtualmin/kaosenlared.net/public_html/

        # This helps applications behind a proxy know that the pages
        # were originally requested over https
        # Prevents browser blocking content because of MIXED CONTENT
        SetEnvIf X-Forwarded-Proto https HTTPS=on

        <LocationMatch "/server-status|/phpfpm-status">
                AllowOverride None
        </LocationMatch>
        <Directory "/home/virtualmin/kaosenlared.net/public_html">
                Options -Indexes
                AllowOverride All
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/wp.error.log
        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn
        CustomLog ${APACHE_LOG_DIR}/wp.access.log proxy

        RewriteEngine On
        RewriteOptions inherit
</VirtualHost>

nginx

root@test-wp-kaos:/etc/nginx/sites-enabled# cat archivo.kaosenlared.net.conf 
server {
    listen 80;
    listen [::]:80;

    server_name archivo.kaosenlared.net;

    include snippets/certbot.conf;
    return 301 https://archivo.kaosenlared.net$request_uri;

    #root /var/www/wp/;
}

server {
    server_name archivo.kaosenlared.net;

    include snippets/certbot.conf;
    #include snippets/capa8-error-pages.conf;

    # Aquí s'inclou el servidor intern i la protecció específica de WP
    include snippets/wordpress.conf;

    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/archivo.kaosenlared.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/archivo.kaosenlared.net/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_trusted_certificate /etc/letsencrypt/live/archivo.kaosenlared.net/chain.pem;
}

Varnish

root@test-wp-kaos:/etc/varnish# cat default.vcl
#
# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide for a comprehensive documentation
# at https://www.varnish-cache.org/docs/.

# Marker to tell the VCL compiler that this VCL has been written with the
# 4.0 or 4.1 syntax.
vcl 4.1;
import std;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

# SET THE ALLOWED IP OF PURGE REQUESTS
# ##########################################################
acl purge {
  "localhost";
  "127.0.0.1";
  "5.10.205.139";
}

sub vcl_recv {
    # Happens before we check if we have this in cache already.
    #
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.
      # set realIP by trimming CloudFlare IP which will be used for various checks
  set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", "");

  # FORWARD THE IP OF THE REQUEST
  if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
      set req.http.X-Forwarded-For =
      req.http.X-Forwarded-For + ", " + client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }

  # Purge request check sections for hash_always_miss, purge and ban
  # BLOCK IF NOT IP is not in purge acl
  # ##########################################################

  # Enable smart refreshing using hash_always_miss
  if (req.http.Cache-Control ~ "no-cache") {
    if (client.ip ~ purge || std.ip(req.http.X-Actual-IP, "127.0.0.1") ~ purge || std.ip(req.http.X-Actual-IP, "5.10.205.136" ) ~ purge )  {
         set req.hash_always_miss = true;
    }
  }

  if (req.method == "PURGE") {
    if (!client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "127.0.0.1") ~ purge || !std.ip(req.http.X-Actual-IP, "5.10.205.136") ~ purge)  {
      return(synth(405,"Not allowed."));
    }
    ban("req.http.host == " + req.http.host +
          " && req.url == " + req.url);
    return (purge);
  }

  if (req.method == "BAN") {
        # Same ACL check as above:
        if (!client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "127.0.0.1") ~ purge || !std.ip(req.http.X-Actual-IP, "5.10.205.136") ~ purge) {
                        return(synth(403, "Not allowed."));
        }
        ban("req.http.host == " + req.http.host +
                  " && req.url == " + req.url);

        # Throw a synthetic page so the
        # request won't go to the backend.
        return(synth(200, "Ban added"));
  }

  # Unset cloudflare cookies
  # Remove has_js and CloudFlare/Google Analytics __* cookies.
  set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");

  # Remove a ";" prefix, if present.
  set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");

  # For Testing: If you want to test with Varnish passing (not caching) uncomment
  # return( pass );

  # DO NOT CACHE RSS FEED
  if (req.url ~ "/feed(/)?") {
    return ( pass );
  }

  # Pass wp-cron

  if (req.url ~ "wp-cron\.php.*") {
    return ( pass );
  }

  ## Do not cache search results, comment these 3 lines if you do want to cache them

  if (req.url ~ "/\?s\=") {
    return ( pass );
  }

  # CLEAN UP THE ENCODING HEADER.
  # SET TO GZIP, DEFLATE, OR REMOVE ENTIRELY.  WITH VARY ACCEPT-ENCODING
  # VARNISH WILL CREATE SEPARATE CACHES FOR EACH
  # DO NOT ACCEPT-ENCODING IMAGES, ZIPPED FILES, AUDIO, ETC.
  # ##########################################################
  if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
      # No point in compressing these
      unset req.http.Accept-Encoding;
    } elsif (req.http.Accept-Encoding ~ "gzip") {
      set req.http.Accept-Encoding = "gzip";
    } elsif (req.http.Accept-Encoding ~ "deflate") {
      set req.http.Accept-Encoding = "deflate";
    } else {
      # unknown algorithm
      unset req.http.Accept-Encoding;
    }
  }

  # PIPE ALL NON-STANDARD REQUESTS
  # ##########################################################
  if (req.method != "GET" &&
    req.method != "HEAD" &&
    req.method != "PUT" &&
    req.method != "POST" &&
    req.method != "TRACE" &&
    req.method != "OPTIONS" &&
    req.method != "DELETE") {
      return (pipe);
  }

  # ONLY CACHE GET AND HEAD REQUESTS
  # ##########################################################
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # OPTIONAL: DO NOT CACHE LOGGED IN USERS (THIS OCCURS IN FETCH TOO, EITHER
  # COMMENT OR UNCOMMENT BOTH
  # ##########################################################
  if ( req.http.cookie ~ "wordpress_logged_in|resetpass" ) {
    return( pass );
  }

  #fix CloudFlare Mixed Content with Flexible SSL
  if (req.http.X-Forwarded-Proto) {
    return(hash);
  }

  # IF THE REQUEST IS NOT FOR A PREVIEW, WP-ADMIN OR WP-LOGIN
  # THEN UNSET THE COOKIES
  # ##########################################################
  if (!(req.url ~ "wp-(login|admin)")
    && !(req.url ~ "&preview=true" )
  ){
    unset req.http.cookie;
  }

  # IF BASIC AUTH IS ON THEN DO NOT CACHE
  # ##########################################################
  if (req.http.Authorization || req.http.Cookie) {
    return (pass);
  }

  # IF YOU GET HERE THEN THIS REQUEST SHOULD BE CACHED
  # ##########################################################
  return (hash);

  # This is for phpmyadmin
  if (req.http.Host == "pmadomain.com") {
    return (pass);
  }

}

sub vcl_hash {
  if (req.http.X-Forwarded-Proto) {
    hash_data(req.http.X-Forwarded-Proto);
  }
}

# HIT FUNCTION
# ##########################################################
sub vcl_hit {
  return (deliver);
}

# MISS FUNCTION
# ##########################################################
sub vcl_miss {
  return (fetch);
}

sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    #
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.
  # I SET THE VARY TO ACCEPT-ENCODING, THIS OVERRIDES W3TC
  # TENDANCY TO SET VARY USER-AGENT.  YOU MAY OR MAY NOT WANT
  # TO DO THIS
  # ##########################################################
  set beresp.http.Vary = "Accept-Encoding";

  # IF NOT WP-ADMIN THEN UNSET COOKIES AND SET THE AMOUNT OF
  # TIME THIS PAGE WILL STAY CACHED (TTL), add other locations or subdomains you do not want to cache here in case they set cookies
  # ##########################################################
  if (!(bereq.url ~ "wp-(login|admin)") && !bereq.http.cookie ~ "wordpress_logged_in|resetpass" ) {
    unset beresp.http.set-cookie;
    set beresp.ttl = 52w;
    set beresp.grace =3d;
  }

  if (beresp.ttl <= 0s ||
    beresp.http.Set-Cookie ||
    beresp.http.Vary == "*") {
      set beresp.ttl = 120s;
      # set beresp.ttl = 120s;
      set beresp.uncacheable = true;
      return (deliver);
  }

  return (deliver);
}

sub vcl_deliver {
    # Happens when we have all the pieces we need, and are about to send the
    # response to the client.
    #
    # You can do accounting or modifying the final object here.
  # IF THIS PAGE IS ALREADY CACHED THEN RETURN A 'HIT' TEXT 
  # IN THE HEADER (GREAT FOR DEBUGGING)
  # ##########################################################
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  # IF THIS IS A MISS RETURN THAT IN THE HEADER
  # ##########################################################
  } else {
    set resp.http.X-Cache = "MISS";
  }
}

La importación de la base de datos también trajo tela y tuve que hacer backup de las tablas de la base de datos por separado. Hice un show tables y ejecuté el dump tabla por tabla. Un ejemplo:

# mysqldump kaosenlared_net_wpXXX --tables wp_posts > wp_posts.sql

Ahora ya lo tenía todo listo para empezar el crawling con httrack, pero a las pocas horas este me decía que la página web tenía demasiados links a analizar, así que tocó empezar a mirar la documentación y finalmente la línea que ejecuté fue esta:

root@test-wp-kaos:/var/www/html/kaos_crawl# httrack https://archivo.kaosenlared.net --advanced-maxlinks=10000000000000 --disable-security-limits --continue

Lo que me generó dentro del directorio kaos_crawl un directorio llamado archivo.kaosenlared.net con todo el contenido a estatico. Para que el cliente pudiese ver la evolución y el resultado, añadí a nginx otro sitio apuntando a estatica.kaosenlared.net.

El proceso lo empecé el 2 de noviembre y a 18 de noviembre aún sigue crawleando la web en local

root@test-wp-kaos:~# ps -eo pid,comm,lstart,etime,time,args
    PID COMMAND         STARTED                  ELAPSED     TIME       COMMAND
   2974 httrack         Tue Nov  2 13:46:51 2021 12-13:14:49 5-06:26:53 httrack https://archivo.kaosenlared.net --advanced-maxlinks=10000000000000 --disable-security-limits --continue

Además voy ejecutando cada dos o tres días el comando para ver el estado del crawling ya que puse el proceso en background

root@test-wp-kaos:~# cd /var/www/html/kaos_crawl/archivo.kaosenlared.net/ && echo "######### HORA ########" && date && echo "######### Nº ENTRADAS ########" && find -type d |wc -l && echo "######### ESPACIO EN DISCO ########" && du -h --max-depth=0

######### HORA ########
Thu 18 Nov 2021 05:43:05 PM CET
######### Nº ENTRADAS ########
347505
######### ESPACIO EN DISCO ########
58G	

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *

Aquest lloc utilitza Akismet per reduir els comentaris brossa. Apreneu com es processen les dades dels comentaris.