1

Starting from Headless Wordpress Starter Kit I am taking two images from the Wordpress media library and drawing them onto a html5 canvas element which I need people to be able to save as an image.

Since the domain of the head is different from the back I will get the error of tainted cross-origin requests. So I set crossOrigin to anonymous. Here is the code where I draw the image to the canvas.

Logo

const image = new Image();
image.setAttribute("crossOrigin", "anonymous");
image.width = logo[size].width;
image.height = logo[size].height;
image.src = `${logo[size].source_url}`;
image.onload = () => {
  const x = (this.width - image.width) / 2;
  this._ctx.drawImage(image, x, x, image.width, image.height);
};

Background Image

const image = new Image(this.width, this.height);
image.setAttribute("crossOrigin", "anonymous");
image.src = backgroundImage[size].source_url;
image.onload = () => {
  const x = image.width / 2;
  this._ctx.drawImage(image, -x, 0);
};

Server / Backend

I didn't edit the CORS set by theme provided in the starter kit.

wp-content/themes/postlight-headless-wp/inc/cors.php

remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );

add_filter( 'rest_pre_serve_request', function ( $value ) {
    header( 'Access-Control-Allow-Origin: ' . get_frontend_origin() );
    header( 'Access-Control-Allow-Methods: GET' );
    header( 'Access-Control-Allow-Credentials: true' );
    return $value;
});

I figured since I was requesting the files directly this script shouldn't be invoked by the server.

Instead I setup a couple docker containers to run a lamp stack. Here's the docker-compose.yaml.

version: "2"

services:
  db:
    container_name: database
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    ports: # Set up ports exposed for other containers to connect to
      - "3306:3306"
    environment: # Set up mysql database name and password
      MYSQL_ROOT_PASSWORD: **********
      MYSQL_DATABASE: **********
      MYSQL_USER: **********
      MYSQL_PASSWORD: **********
  wordpress:
    build: .
    container_name: wordpress
    depends_on:
      - db
    ports:
      - "4000:80"
    volumes:
      - ./html:/var/www/html
    links:
      - db
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_PASSWORD: **********
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    depends_on:
      - db
    restart: always
    ports:
      - "8080:80"
    environment:
      - PMA_ARBITRARY=1
volumes:
  db_data:

At the root of the html folder I added the .htaccess file.

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

<IfModule mod_setenvif.c>
  <IfModule mod_headers.c>
    <FilesMatch "\.(bmp|cur|gif|ico|jpe?g|png|svgz?|webp)$">
      SetEnvIf Origin ":" IS_CORS
      Header set Access-Control-Allow-Origin "*" env=IS_CORS
    </FilesMatch>
  </IfModule>
</IfModule>
# END WordPress

With all of this I expected that I wouldn't have a problem. But I'm here, and I've typed all this, so, yeah, I have a problem. The background images work perfectly, the logo images don't.

Chrome 70 - Nope.

Access to image at 'http://localhost:4000/wp-content/uploads/2018/10/paintWithNoBackground-1-150x150.png' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Safari 12 - Nope.

[Error] Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin.
[Error] Cannot load image http://localhost:4000/wp-content/uploads/2018/10/paintWithNoBackground-1-150x150.png due to access control checks.
[Error] Failed to load resource: Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin. (paintWithNoBackground-1-150x150.png, line 0)

Firefox 63 - Yes, it works here

No errors.

In all browsers the background image works just fine. I don't know why one would work and the other not, and then why it works in Firefox but not Chrome, or Safari. Let me know if you need any other information.

kalm42
  • 784
  • 9
  • 19
  • What does the method `get_frontend_origin()` return? You could try to change that header to `'Access-Control-Allow-Origin: "*"'`. I would also consider adding `OPTIONS` to the allowed methods. – Justin Collier Nov 01 '18 at 14:18
  • `get_frontend_origin()` returns the FQDN of the frontend. I will try both of your suggestions. Thank you so much. – kalm42 Nov 01 '18 at 17:25
  • looking at this `` in .htaccess where the header is set, it looks like you only pass cors headers on the response object that includes these file types. But the options response will also need this header to pass pre-flight. – Justin Collier Nov 02 '18 at 20:16

2 Answers2

2

Ok, so I do think it's possible. The issue I had here was.. the cache. Browser cached the call without the 'origin' header, which basically made the server to respond without the CORS headers. See: https://stackoverflow.com/a/17570351/3391743

After adding the
<IfModule mod_setenvif.c> <IfModule mod_headers.c> <FilesMatch "\.(bmp|cur|gif|ico|jpe?g|png|svgz?|webp)$"> SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS </FilesMatch> </IfModule> </IfModule> and cleaning the cache it started working as it should, with images displaying and not tainting the canvas that I was writing the images to. Yay!

Piotr J.
  • 131
  • 4
0

You don't. This isn't possible. Both code bases must be hosted under the same domain. I say this after working on this problem for days.

I'll leave the question unmarked incase someone (not me) figures it out.

kalm42
  • 784
  • 9
  • 19