CORS y Apache como Reverse Proxy

   Hace algún tiempo vengo usando Nginx como Reverse Proxy, sin embargo me dio curiosidad hacer lo mismo con Apache2 y no fue tan complicado, ¿o si?. Bien, lo primero es habilitar los módulos (he colocado todos estos a modo de prueba):
a2enmod proxy proxy_http proxy_ajp rewrite deflate headers proxy_balancer proxy_connect proxy_html
   Luego crear el vhost (recalco que estoy utilizando SSL de letsencrypt):
<VirtualHost *:80>
    ServerName misitio.com
    ServerAlias www.misitio.com
    Redirect / https://misitio.com/
</VirtualHost>

<VirtualHost *:443>
    SSLEngine On
    SSLProxyEngine On

    SSLCertificateFile /etc/letsencrypt/live/misitio.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/misitio.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/misitio.com/chain.pem

    ServerName misitio.com
    ServerAlias www.misitio.com

    ErrorLog ${APACHE_LOG_DIR}/misitio-error.log
    CustomLog ${APACHE_LOG_DIR}/misitio-access.log combined
</VirtualHost>

   Ahora solo debemos agregar las líneas necesarias para el proxy:
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/

   ¿No ha sido dificil verdad?, pero espera, probando me encuentro con un error particular: Cross-Origin Request Blocked... De acuerdo, este error es común, solo debemos agregar las cabeceras:
Header add Access-Control-Max-Age "1000"
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
Header add Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"

   Probando de nuevo llegamos al punto de otro error: Method not Allowed. En este punto es cuando nos topamos con un tema curioso, preflight. Entonces es cuando debemos modificar las cabeceras para que siempre estén los valores:
Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"

   Probamos y..... Error. Las peticiones GET procesan pero el POST es otro asunto, en este punto es cuando el dichoso "preflight" nos da dolores de cabeza, entonces debemos colocar una condición para este tipo de petidiones:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
   Volviendo a probar, resultado: Error!. Pero ¿porque?, respuesta simple (gracias a user5994461):
Es un error en Apache. Un fallo en el diseño interno y un fallo en documentarlo. Hay al menos dos tablas de cookies en apache:

  onsuccess: por defecto, usado para códigos de estado 20X.
  always: usado para errores, incluyendo códigos de redirecciones.

Las tablas tienen diferentes significados dependiendo de qué módulos están en uso. Por ejemplo, cuando se utiliza proxy o CGI, la tabla relevante para las cookies es "onsuccess" si el servidor entrega un error con éxito, pero "always" si ocurre un error interno de apache.

Este comportamiento no está documentado. Eso no parece intencional sino una consecuencia del propio apache. En el estado actual, es básicamente imposible manipular correctamente los encabezados con Apache.

   Una vez visto esto solo queda una solución posible, eliminar las cabeceras y volverlas a setear justo antes de finalizar la petición:
Header onsuccess unset Access-Control-Allow-Origin
Header onsuccess unset Access-Control-Allow-Methods
Header onsuccess unset Access-Control-Allow-Headers

   Por lo que nuestro "conf" quedaría de la siguiente forma:
<VirtualHost *:80>
    ServerName misition.com
    ServerAlias www.misitio.com
    Redirect / https://misitio.com/
</VirtualHost>

<VirtualHost *:443>
    SSLEngine On
    SSLProxyEngine On

    SSLCertificateFile /etc/letsencrypt/live/misitio.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/misitio.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/misitio.com/chain.pem

    ProxyPreserveHost On
    ProxyRequests off

    <proxy *>
        Order deny,allow
        Allow from all
    </proxy>

    ProxyPass / http://127.0.0.1:8080/
    ProxyPassReverse / http://127.0.0.1:8080/

    Header onsuccess unset Access-Control-Allow-Origin
    Header onsuccess unset Access-Control-Allow-Methods
    Header onsuccess unset Access-Control-Allow-Headers

    Header always set Access-Control-Max-Age "1000"
    Header always set Access-Control-Allow-Origin "*"
    Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"

    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]

    ServerName misitio.com
    ServerAlias www.misitio.com

    ErrorLog ${APACHE_LOG_DIR}/misitio-error.log
    CustomLog ${APACHE_LOG_DIR}/misitio-access.log combined
</VirtualHost>

   Si con todo esto no solucionamos entonces lo mas recomendable es migrar a Nginx, muchas horas depurando y con resultados que no son los esperados. Fuentes:



Lamentablemente hay muchos usuarios en la red que han llegado al blog para escribir obscenidades, así que la moderación se hace necesaria. Recuerda utilizar un lenguaje correcto y espera a que sea aprobado.

Si necesitas publicar código haz click en "Conversión" para hacerlo legible.
ConversiónConversión