Custom NGINX configuration

I’m trying to use a Wordpress plugin that converts existing image files into webp and then uses an nginx rule to try and get the webp version. If the webp version fails it reverts to default. Is it possible to do this with platform?

location ~ /wp-content/(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$ {
    add_header Vary Accept;
    expires 365d;
    try_files
        /wp-content/uploads-webpc/$path.$ext$ext_webp
        $uri =404;
}

I’ve tried several variations using locations in app.yaml but the closest I get is this in the nginx.conf on pkatform


    location "/wp/wp-content/uploads/" {
      alias /app/web/wp/wp-content/uploads/;

      location "/wp/wp-content/uploads/" {

        try_files       $uri =404;
        expires         -1s;

      }

      location ~ "(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$" {


        location ~ "(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$" {

        set $_rewrite_path "/wp/wp-content/uploads-webpc/uploads/$path.$ext";
        try_files       $uri @rewrite;
        expires         -1s;

        }
      }



    }

Thanks Dan

Hi Dan, take a look at Rewrite Requests without Redirect documentation.

You should end up with something like this in your application yaml:

web:
    locations:
        '/':
            ...
            rules:
                '^/wp-content/(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$':
                    passthru: '/wp-content/uploads-webpc/$path.$ext$ext_webp'

Hope this helps. Good luck!

I’ve tried for a fair few hours to get this working but can’t, I even tried just re-writing a single file I know exits in both formats but can’t get that to work either.

for example:

            '^/wp/wp-content/uploads/2022/06/Product-Footer-2.jpg':
              passthru: '/wp/wp-content/uploads-webpc/uploads/2022/06/Product-Footer-2.jpg.webp'

I’m using the Wordpress Bedrock template which may be causing some confusion. My whole locations config looks like this


web:
    locations:
        "/":
            # The public directory of the app, relative to its root.
            root: "web"
            # The front-controller script to send non-static requests to.
            passthru: "/index.php"
            # Wordpress has multiple roots (wp-admin) so the following is required
            index:
                - "index.php"
            # The number of seconds whitelisted (static) content should be cached.
            expires: 31536000
            scripts: true
            allow: true
            rules:
                ^/composer\.json:
                    allow: false
                ^/license\.txt$:
                    allow: false
                ^/readme\.html$:
                    allow: false
                '^/wp-content/(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$':
                    passthru: '/wp-content/uploads-webpc/$path.$ext.webp'
        "/wp/wp-content/cache":
            root: "web/wp/wp-content/cache"
            scripts: false
            allow: false
        "/wp/wp-content/uploads":
            root: "web/app/uploads"
            scripts: false
            allow: false
            rules:
                # Allow access to common static files.
                '(?<!\-lock)\.(?i:jpe?g|gif|png|svg|bmp|ico|css|js(?:on)?|eot|ttf|woff|woff2|pdf|docx?|xlsx?|pp[st]x?|psd|odt|key|mp[2-5g]|m4[av]|og[gv]|wav|mov|wm[av]|avi|3g[p2])$':
                    allow: true
                    expires: 1w
        "/wp/wp-content/uploads-webpc/uploads":
            root: "web/app/uploads-webpc/uploads"
            scripts: false
            allow: true

Cheers Dan

Sorry, my original suggestion was not correct. The root of the problem that I suggested is that passthru must be a web URL path that your app is ready to handle—not a local file-path route.

I suggest starting fresh from the original .platform.app.yaml provided by the WordPress Bedrock template and then following the steps below.

Let’s first ensure that web requests for your uploads-webpc can be answered. This location will be referenced by your /wp/wp-content rule.

Note: that Wordpress Bedrock uses the file path web/app, not web/wp-content

Add this to your web.locations section:

    "/wp/uploads-webpc":
      root: "web/app/uploads-webpc"
      scripts: false
      allow: false
      rules:
        '(?<!\-lock)\.(?i:jpe?g|webp|gif|png|svg|bmp|ico|css|js(?:on)?|eot|ttf|woff|woff2|pdf|docx?|xlsx?|pp[st]x?|psd|odt|key|mp[2-5g]|m4[av]|og[gv]|wav|mov|wm[av]|avi|3g[p2])$':
          allow: true
          expires: 1w

This is essentially the same setup as the wp/wp-content rule. The only difference is the root location and webp has been added to the rules.

Now, let’s ensure /wp/wp-content/uploads checks for webp files

Modify the existing /wp/wp-content/uploads location to include a second rule. It passes request through the location we defined above—looking for the file name requested, ending with .webp

'^\/wp\/wp-content\/(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$':
    passthru: '/wp/uploads-webpc/$path.$ext.webp'

Finally, if you are using the webp-converter-for-media plugin to do this, ensure that it can create files as needed by adding this to your mounts:

"web/app/uploads-webpc":
     source: local
     source_path: "uploads-webpc"

I tested this setup using the webp-converter-for-media via composer require wpackagist-plugin/webp-converter-for-media. I’m unfamiliar with this package, so I may have missed something. Hopefully, this gives you an idea of how to iterate through testing these route rewrites.

Good luck!

Hi Tyler,

Thanks for the pointers, I re-configured the locations and mounts correctly as I had got them mixed up from when I first setup the site a while back.

I am using the plugin/webp-converter-for-mdeia plugin and can confirm that my webp images are being served on the alternative path they’re just not being re-written / redirected.

For example:
https://feature-fix-webp-gj4zkma-qn5amk3ilbjc2.uk-1.platformsh.site//app/uploads/2022/06/Product-Footer-2.jpg
https://feature-fix-webp-gj4zkma-qn5amk3ilbjc2.uk-1.platformsh.site//app/uploads-webpc/uploads/2022/06/Product-Footer-2.jpg.webp

But on the landing page the images in the source are still the originals.
https://feature-fix-webp-gj4zkma-qn5amk3ilbjc2.uk-1.platformsh.site

My locations are setup as follows.

web:
    locations:
        "/":
            # The public directory of the app, relative to its root.
            root: "web"
            # The front-controller script to send non-static requests to.
            passthru: "/index.php"
            # Wordpress has multiple roots (wp-admin) so the following is required
            index:
                - "index.php"
            # The number of seconds whitelisted (static) content should be cached.
            expires: 31536000
            scripts: true
            allow: true
            rules:
                ^/composer\.json:
                    allow: false
                ^/license\.txt$:
                    allow: false
                ^/readme\.html$:
                    allow: false
        "/wp/wp-content/cache":
            root: "web/app/cache"
            scripts: false
            allow: false
        "/wp/wp-content/uploads":
            root: "web/app/uploads"
            scripts: false
            allow: false
            rules:
                # Allow access to common static files.
                '(?<!\-lock)\.(?i:jpe?g|webp|gif|png|svg|bmp|ico|css|js(?:on)?|eot|ttf|woff|woff2|pdf|docx?|xlsx?|pp[st]x?|psd|odt|key|mp[2-5g]|m4[av]|og[gv]|wav|mov|wm[av]|avi|3g[p2])$':
                    allow: true
                    expires: 1w
                '^\/wp\/wp-content\/(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$':
                    passthru: ''/wp/wp-content/uploads-webpc/$path.$ext.webp'
        "/wp/wp-content/uploads-webpc/uploads":
            root: "web/app/uploads-webpc/uploads"
            scripts: false
            allow: false
            rules:
                '(?<!\-lock)\.(?i:jpe?g|webp|gif|png|svg|bmp|ico|css|js(?:on)?|eot|ttf|woff|woff2|pdf|docx?|xlsx?|pp[st]x?|psd|odt|key|mp[2-5g]|m4[av]|og[gv]|wav|mov|wm[av]|avi|3g[p2])$':
                    allow: true
                    expires: 1w

I think I’m close but I just can’t get the pattern working.

Thanks Dan

TL;DR: Jump to the “Now, let’s approach this logically.” summary at the end of this post :slight_smile:


I’m familiarizing myself with WordPress Bedrock as we talk through this, so sorry for anything I miss along the way.

It looks like Bedrock doesn’t actually use /wp/wp-content/uploads - it will always be referred to as /app/uploads. So we need to tell platform.sh how to hand requests to /app/uploads URLs when they include .webp extensions. Once you find success there, you can repeat the process for /app/themes and /app/plugins requests if you choose to take advantage of those plugin features.

  1. It looks like requests to /app/uploads-webpc and /app/uploads work out-of-the-box with this template. For example, if I upload test_file.jpg and request the URL /app/uploads/2022/11/test_image-150x150.jpg.webp, it loads without issue. If I generate the webpc version of the file, I can access that URL as well.
    That means we don’t need the special rule we added for uploads-webpc to answer requests. Let’s delete this from the yaml:
    "/wp/wp-content/uploads-webpc/uploads":
       root: "web/app/uploads-webpc/uploads"
       scripts: false
       allow: false
       rules:
         '(?<!\-lock)\.(?i:jpe?g|webp|gif|png|svg|bmp|ico|css|js(?:on)?|eot|ttf|woff|woff2|pdf|docx?|xlsx?|pp[st]x?|psd|odt|key|mp[2-5g]|m4[av]|og[gv]|wav|mov|wm[av]|avi|3g[p2])$':
           allow: true
           expires: 1w
    
  2. We know that WordPress Bedrock is requesting media from uploads via this route: /app/uploads/....
    • So, let’s remove this from /wp/wp-content/uploads rule:
      '^\/wp\/wp-content\/(?<path>.+)\.(?<ext>jpe?g|png|gif|webp)$':
          passthru: '/wp/wp-content/uploads-webpc/$path.$ext.webp'
      

Now, let’s approach this logically.

  • What do we want to happen when /app/uploads receives a .webp file request?
    • We do not want to look for that file in the web/app/uploads directory`
    • Instead, look for that file in web/app/uploads-webpc/uploads directory
  • What do we want to happen when /app/uploads-webpc receives a request?
    • Allow any file to be accessed in web/app/uploads-webpc
    • Block any script files from executing
    • If the request is to a .webp file and the file was not found, look for it in web/app/uploads.

Using your .platform.app.yaml file, the result is this. Note the change to the app/uploads changes and the cleanup of the wp/ locations that we previously created.


# The configuration of app when it is exposed to the web.
web:
  locations:
    "/":
      root: "web"
      passthru: "/index.php"
      index:
        - "index.php"
      expires: 31536000
      scripts: true
      allow: true
      rules:
        ^/composer\.json:
          allow: false
        ^/license\.txt$:
          allow: false
        ^/readme\.html$:
          allow: false
        ^\/app\/uploads\/(?<path>.+)\.webp$: # What do we do when a .webp file is requested?
          allow: false # DO NOT look for it in the /app/uploads directory
          passthru: '/app/uploads-webpc/uploads/$path.webp' # Instead, Look for the file in /app/uploads-webpc
    "/app/uploads-webpc": # What do we do when a URL requested to /all/uploads-webpc is made?
        allow: true # Allow any file in this directory to be viewed
        scripts: false # Do not allow scripts to run
        rules:
          ^\/app\/uploads\/(?<path>.+)\.webp$: # What do we do when a .webp file is requested?
            allow: true # Try to loaded it if it exists
            passthru: '/app/uploads/$path' # If it is not found, check for the original version in /app/uploads

    "/wp/wp-content/cache":
      root: "web/app/cache"
      scripts: false
      allow: false
    "/wp/wp-content/uploads":
      root: "web/app/uploads"
      scripts: false
      allow: false
      rules:
        '(?<!\-lock)\.(?i:jpe?g|webp|gif|png|svg|bmp|ico|css|js(?:on)?|eot|ttf|woff|woff2|pdf|docx?|xlsx?|pp[st]x?|psd|odt|key|mp[2-5g]|m4[av]|og[gv]|wav|mov|wm[av]|avi|3g[p2])$':
          allow: true
          expires: 1w

Edit

Just to be clear, this will look for the webp version first, and fall back to the original version only when the .webp extension is included in the URL. If file.jpg is requested, it will not look for the file.jpg.webp version. If that is what is needed, it shouldn’t be too tricky to modify the existing logic to do so.