oc-responsive-images-plugin icon indicating copy to clipboard operation
oc-responsive-images-plugin copied to clipboard

Webp .htaccess not working

Open mauserrifle opened this issue 5 years ago • 8 comments

First thanks for the amazing work!

I could not get the webp .htaccess rules working. I had to add the following lines to some existing OctoberCMS rules:

RewriteCond %{REQUEST_URI} !webp.php$

Without this apache2 would parse all the rules multiple times. I even had situations I reached the max 10 internal redirects.

I also had alot of issues with the DOCUMENT_ROOT rewrites (slash issues, I think dependend on how the document_root is set invhost). Eventually I picked the rules from the Wordpress webp express plugin:

    # Redirect to existing converted image in same dir (if browser supports webp)
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_FILENAME}.webp -f
    RewriteRule ^/?(.*)\.(jpe?g|png)$ $1.$2.webp [NC,T=image/webp,E=EXISTING:1,E=ADDVARY:1,L]


    # Redirect images to webp-on-demand.php (if browser supports webp)
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteRule ^(/?(.+)\.(jpe?g|png))$ /plugins/offline/responsiveimages/webp.php?path=$1 [NC,L,E=REQFN:%{REQUEST_FILENAME},E=WPCONTENT:app]

With these changes everything worked perfectly! :)

Hope you understand the issues I have had and find it useful to improve things.

My full .htaccess:

## START OFFLINE.ResponsiveImages - webp-rewrite
#  DO NOT REMOVE THESE LINES
<IfModule mod_setenvif.c>
    # Vary: Accept for all the requests to jpeg and png
    SetEnvIf Request_URI "\.(jpe?g|png)$" REQUEST_image
</IfModule>
<ifModule mod_rewrite.c>
    RewriteEngine On

    # Redirect to existing converted image in same dir (if browser supports webp)
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_FILENAME}.webp -f
    RewriteRule ^/?(.*)\.(jpe?g|png)$ $1.$2.webp [NC,T=image/webp,E=EXISTING:1,E=ADDVARY:1,L]


    # Redirect images to webp-on-demand.php (if browser supports webp)
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteRule ^(/?(.+)\.(jpe?g|png))$ /plugins/offline/responsiveimages/webp.php?path=$1 [NC,L,E=REQFN:%{REQUEST_FILENAME},E=WPCONTENT:app]

</ifModule>
<IfModule mod_headers.c>
    Header append Vary Accept env=REQUEST_image
</IfModule>
<IfModule mod_mime.c>
    AddType image/webp .webp
</IfModule>
## END OFFLINE.ResponsiveImages - webp-rewrite

<IfModule mod_rewrite.c>

    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    ##
    ## You may need to uncomment the following line for some hosting environments,
    ## if you have installed to a subdirectory, enter the name here also.
    ##
    # RewriteBase /

    ##
    ## Uncomment following lines to force HTTPS.
    ##
    # RewriteCond %{HTTPS} off
    # RewriteRule (.*) https://%{SERVER_NAME}/$1 [R,L]

    ##
    ## Black listed folders
    ##
    RewriteCond %{REQUEST_URI} !webp.php$
    RewriteRule ^bootstrap/.* index.php [L,NC]
    RewriteRule ^config/.* index.php [L,NC]
    RewriteRule ^vendor/.* index.php [L,NC]
    RewriteRule ^storage/cms/.* index.php [L,NC]
    RewriteRule ^storage/logs/.* index.php [L,NC]
    RewriteRule ^storage/framework/.* index.php [L,NC]
    RewriteRule ^storage/temp/protected/.* index.php [L,NC]
    RewriteRule ^storage/app/uploads/protected/.* index.php [L,NC]

    ##
    ## White listed folders
    ##
    RewriteCond %{REQUEST_URI} !webp.php$
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteCond %{REQUEST_FILENAME} !/.well-known/*
    RewriteCond %{REQUEST_FILENAME} !/storage/app/uploads/.*
    RewriteCond %{REQUEST_FILENAME} !/storage/app/media/.*
    RewriteCond %{REQUEST_FILENAME} !/storage/temp/public/.*
    RewriteCond %{REQUEST_FILENAME} !/themes/.*/(assets|resources|dist)/.*
    RewriteCond %{REQUEST_FILENAME} !/plugins/.*/(assets|resources)/.*
    RewriteCond %{REQUEST_FILENAME} !/modules/.*/(assets|resources)/.*
    RewriteRule !^index.php index.php [L,NC]

    ##
    ## Block all PHP files, except index
    ##
    RewriteCond %{REQUEST_URI} !webp.php$
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteCond %{REQUEST_FILENAME} \.php$
    RewriteRule !^index.php index.php [L,NC]

    ##
    ## Standard routes
    ##
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]

</IfModule>

mauserrifle avatar Nov 21 '19 16:11 mauserrifle

Thank you very much for your feedback!

RewriteCond %{REQUEST_URI} !webp.php$

That's strange, this rule should not be required since the .htaccess should never reach October's PHP blacklist rules.

I have updated the rules a bit, would you mind testing if this works on your server (without the explicit whitelisting of the webp.php?) If I remove the %{DOCUMENT_ROOT} in the webp.php RewriteRule, it no longer works on my server :thinking:

https://github.com/OFFLINE-GmbH/oc-responsive-images-plugin/blob/develop/views/webp-rewrite.htm#L6-L17

tobias-kuendig avatar Nov 22 '19 07:11 tobias-kuendig

The latest updates work. But only worked with the RewriteCond %{REQUEST_URI} !webp.php$ lines. Thus I searched further. This behavior is documented by apace2 at: https://httpd.apache.org/docs/2.4/rewrite/flags.html#flag_l

So we need to use the END flag instead of L :+1:

It works using:

RewriteRule (.+)$ %{DOCUMENT_ROOT}/plugins/offline/responsiveimages/webp.php?path=$1 [NC,END]

Other point: It's important to only match the storage folder. For example. My theme images are now always served using webp.php. Example theme file:

/themes/XXXX/assets/dist/app/apple-touch-icon.png

which is not a good thing to do. These files should just be served static.

mauserrifle avatar Nov 22 '19 10:11 mauserrifle

Wow, thank you for this input! I did not know about the END flag. Also, very good point concerning the storage folder. I came up with this new ruleset:

<ifModule mod_rewrite.c>
    RewriteEngine On

    # If the Browser supports WebP images, and the .webp file exists, use it.
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_URI} ^/?storage/.*\.(jpe?g|png)
    RewriteCond %{REQUEST_FILENAME}.webp -f
    RewriteRule ^/?(.*)$ $1.webp [NC,T=image/webp,END]

    # If the Browser supports WebP images, and the .webp file does not exist, generate it.
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_URI} ^/?storage/.*\.(jpe?g|png)
    RewriteCond %{REQUEST_FILENAME}\.webp !-f
    RewriteRule ^/?(.*)$ %{DOCUMENT_ROOT}/plugins/offline/responsiveimages/webp.php?path=$1 [NC,END]
</ifModule>

tobias-kuendig avatar Nov 22 '19 12:11 tobias-kuendig

Yep this is it :smiley: . Works great!

mauserrifle avatar Nov 22 '19 13:11 mauserrifle

I also made a further little change to my project rules which makes sure only resized images are served as webp (RewriteCond %{REQUEST_URI} ^/?storage/.*/public/.*\.(jpe?g|png)). But is a personal preference. I had big original images that were converted to webp, but timed out during the proces.

mauserrifle avatar Dec 09 '19 07:12 mauserrifle

I think the main problem is, that the plugin creates a new file with the file extension itself. For example image.jpg becomes image.jpg.webp and the htaccess file recognize the following rule as true:

    RewriteCond %{REQUEST_URI} ^/?storage/.*\.(jpe?g|png)

The plugin should create the new file without the jpg extension. I added a webp extension rule to exclude this case like so: see # !important!

## START OFFLINE.ResponsiveImages - webp-rewrite
#  DO NOT REMOVE THESE LINES
<IfModule mod_setenvif.c>
    # Vary: Accept for all the requests to jpeg and png
    SetEnvIf Request_URI "\.(jpe?g|png)$" REQUEST_image
</IfModule>
<ifModule mod_rewrite.c>
    RewriteEngine On

    # If the Browser supports WebP images, and the .webp file exists, use it.
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_URI} ^/?storage/.*\.(jpe?g|png)$
    RewriteCond %{REQUEST_FILENAME}\.webp -f
    RewriteRule ^(.*)$ $1.webp [L,T=image/webp,R=301]

    # If the Browser supports WebP images, and the .webp file does not exist, generate it.
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_URI} ^/?storage/.*\.(jpe?g|png)
    RewriteCond %{REQUEST_URI} !\.(webp)$ # !important!
    RewriteCond %{REQUEST_FILENAME}.webp !-f
    RewriteRule ^/?(.*)$ plugins/offline/responsiveimages/webp.php?path=$1 [N,END]


</ifModule>
<IfModule mod_headers.c>
    Header append Vary Accept env=REQUEST_image
</IfModule>
<IfModule mod_mime.c>
    AddType image/webp .webp
</IfModule>
## END OFFLINE.ResponsiveImages - webp-rewrite

schillerenrico avatar Feb 23 '21 17:02 schillerenrico

I'm drawing a blank. I currently fail to see the problem.

The frontend will issue a request for /stroage/any/file.jpg, since this is in your markup. The htaccess rule sees that and checks for the same filename witht he webp extension. If it exists, it serves it. Otherwise it will redirect the request to the webp.php.

Where do things go wrong?

tobias-kuendig avatar Feb 24 '21 06:02 tobias-kuendig

Thats working great and is not the problem :) Requests like "...file.jpg.webm" are handled like a .jpg file not like a webm file. In my case I wanted the url to be the real webm path, so I did a 301 to the webm file if an image is requested and a webm does exists. If a webm doesnt exist, your case will trigger and create it.

schillerenrico avatar Mar 01 '21 13:03 schillerenrico