Go to main content

Textpattern CMS support forum

You are not logged in. Register | Login | Help

#1 2022-10-18 17:55:33

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,412
Website

rewriting REQUEST_URI redirect using pretext callback [SOLVED]

With your help, I used this method to get a tidy solution working in this thread for making forms callable via a clean url.

This time, I’m trying to strip out a file revision timestamp from a js or css asset as a means of cache busting. I have something set up along these lines that produces an url like this but the file has no timestamp in the actual filename:

https://domain.com/path/to/assets/css/styles.1666040041.min.css

where 1666040041 is the file timestamp (both with and without .min) and I want textpattern to use the actual file:

https://domain.com/path/to/assets/css/styles.min.css

—-

I did first try using htaccess using this directive:

RewriteRule ^(.+)\.(\d+)\.(min\.)?(css|js)$   $1.$3$4

and while both regex101.com and htaccess.madewithlove.com give positive results, I had absolutely no luck getting it to work on the server’s htaccess and ran out of ideas. Suggestions welcome!

—-

So I tried turning to the pretext callback, which has the advantage of being more portable too. From the thread linked at the start, I gather I have to append 1 to the end of the callback and then rewrite $_SERVER['REQUEST_URI'] because $pretext is not yet available, and I guess not relevant as the asset isn’t content handled by the CMS. The plugin is set to load order 4 in manifest.json and my previous htaccess attempts are not active.

But I must still be missing something as I get no useful response and putting the asset link directly in the url returns a 404.

This is what I have so far. I know it’s a bit long-winded but I wanted to make sure each part worked.

<?php
if (defined("txpinterface") && txpinterface == "public") {
    register_callback("txb_filerev_cleaner", "pretext", "", 1); // end with , '', 1 if rewriting REQUEST_URI
}

function txb_filerev_cleaner()
{
    // Set to true for debug output
    $debug = false;

    $uri_parts = explode('/', ltrim(serverSet("REQUEST_URI")));
    $last_uripart = end($uri_parts);

            // debug: Incoming Request_uri
            if ($debug) {
                dmp("Request_uri incoming: " . serverSet("REQUEST_URI"));
                print "<pre>\$uri_parts: ";
                print_r($uri_parts);
                print "</pre>";
            }

    // is this a .js or .css file?
    if (preg_match('/\.(css|js)$/', $last_uripart)) {
        // Yes, then strip numeric timestamp from url part
        $last_uripart = preg_replace(
            '/(\w+)\.(\d+)\.(min\.)?(css|js)/',
            '$1.$3$4',
            $last_uripart
        );
        // drop timestamped last uripart
        array_pop($uri_parts);
        // reconstruct cleaned uri
        $uri_stub = implode('/', $uri_parts);
        $req = $uri_stub . '/' . $last_uripart;

        // reset server request_uri
        $_SERVER['REQUEST_URI'] = $req;
    }

            // debug: cleaned Request_uri
            if ($debug) {
                dmp("Last uri part: " . $last_uripart);
                dmp("Request_uri rewritten: " . serverSet("REQUEST_URI"));
            }

}

With debug = true; it outputs a correct rewrite:

Request_uri incoming: /themes/theme-name/assets/css/styles.1666086073.min.css
$uri_parts: Array
(
    [0] => 
    [1] => themes
    [2] => theme-name
    [3] => assets
    [4] => css
    [5] => styles.1666040041.min.css
)
Last uri part: styles.min.css
Request_uri rewritten: /themes/theme-name/assets/css/styles.min.css

Is there another step needed to do something to ensure the resource is loaded?


TXP Builders – finely-crafted code, design and txp

Offline

#2 2022-10-18 18:57:53

etc
Developer
Registered: 2010-11-11
Posts: 4,701
Website GitHub

Re: rewriting REQUEST_URI redirect using pretext callback [SOLVED]

Have you tried adding [PT, L] flag to your RewriteRule? Otherwise, the server will continue processing them, and you will end at index.php. I mean, unless you put your block before the standard txp rule

    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^(.+) - [PT,L]

Offline

#3 2022-10-18 21:58:18

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,412
Website

Re: rewriting REQUEST_URI redirect using pretext callback [SOLVED]

Well, it seems I found the answer on the htaccess front, albeit a bit by chance.

The css and js files in question are in an /assets/ folder inside the theme in the themes folder, and it seems the following directive in the second .htaccess inside the /themes folder is causing the problem:

# Inhibit direct file downloads.
RewriteRule .*\.txp$ - [R=403,L]

Commenting that line out allows the primary htaccess to work as designed. Any idea why that may be? or what I can do to reinstate it’s functionality?

FWIW this is what I have in the main .htaccess file. I put the asset timestamp rewrite line ahead of Textpattern’s directives. I only had the [L] flag originally and I thought that would suffice. html5boilerplate suggest preceding it with a condition that, if I’ve understood it correctly, means only act on the rewrite if the file is not found, which presumably reduces server load/time.

<IfModule mod_rewrite.c>
    RewriteEngine On

    #Options +FollowSymlinks

    #RewriteBase /relative/web/path/

    # Force https://
    # See https://github.com/h5bp/server-configs-apache/ about possible exceptions
    RewriteCond %{HTTPS} !=on
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

    # Filename-based cache busting, e.g.: styles.12345678(.min).css -> styles(.min).css
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.+)\.(\d+)\.(min\.)?(css|js)$  $1.$3$4 [PT,L]

    # Textpattern
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^(.+) - [PT,L]

    RewriteCond %{REQUEST_URI} !=/favicon.ico
    RewriteRule ^(.*) index.php

    RewriteCond %{HTTP:Authorization}  !^$
    RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}]
</IfModule>

Thanks for prodding me to investigate it again.


TXP Builders – finely-crafted code, design and txp

Offline

#4 2022-10-18 22:04:30

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,412
Website

Re: rewriting REQUEST_URI redirect using pretext callback [SOLVED]

Is there another step needed to do something to ensure the resource is loaded?

I’d still be keen to know if I could do the rewrite / cleaning the url via a pretext callback, and whether I’ve understood the principle properly. I have the feeling I’m missing something key, like causing the file to be refetched using revised REQUEST_URI…

I use laravel valet as my local webserver which runs nginx behind the scenes, though you’re not exposed to it. As such the .htaccess files is ignored on my own machine meaning that the same code doesn’t work online and locally.


TXP Builders – finely-crafted code, design and txp

Offline

#5 2022-10-19 07:08:16

etc
Developer
Registered: 2010-11-11
Posts: 4,701
Website GitHub

Re: rewriting REQUEST_URI redirect using pretext callback [SOLVED]

jakob wrote #333986:

I’d still be keen to know if I could do the rewrite / cleaning the url via a pretext callback, and whether I’ve understood the principle properly. I have the feeling I’m missing something key, like causing the file to be refetched using revised REQUEST_URI…

The access to existing files is handled by .htaccess, passed this point it is too late. Textpattern itself will not redirect you to REQUEST_URI (rewritten or not) location. It will just parse it as a standard txp URL and 404 if no URL scheme matches. But you can do this redirection in your plugin:

header('Location: your_rewritten_url');

Still, I would prefer a .htaccess-like solution, since launching php just to redirect is a waist of cycles.

Offline

#6 2022-10-19 08:23:54

jakob
Admin
From: Germany
Registered: 2005-01-20
Posts: 4,412
Website

Re: rewriting REQUEST_URI redirect using pretext callback [SOLVED]

etc wrote #333990:

header('Location: your_rewritten_url');...

Thank you! That was the (now obvious) missing piece. With exit; tagged on afterwards, it works 🎉

This is the trimmed down working version:

<?php
if (defined("txpinterface") && txpinterface == "public") {
    register_callback("txb_filerev_cleaner", "pretext", "", 1); // end with , '', 1 if rewriting REQUEST_URI
}

function txb_filerev_cleaner()
{
    // debug: set to true and enter asset url in browser
    $debug = false;

    $req_incoming = serverSet("REQUEST_URI");

    if ($debug) { dmp("Request_uri:  " . $req_incoming); }

    // is this a .js or .css file?
    if (preg_match('/\.(css|js)$/', $req_incoming)) {
        // Yes, then strip numeric timestamp from url
        $req = preg_replace(
            '/^(.+)\.(\d+)\.(min\.)?(css|js)$/',
            '$1.$3$4',
            $req_incoming
        );

        if ($debug) { dmp("New location: " . $req); exit; }

        header('Location:' . $req);
        exit;
    }
}

Still, I would prefer a .htaccess-like solution, since launching php just to redirect is a waist of cycles.

Understood. Still, it’s useful because I can include this as a separate plugin on my local webserver and still work with the same codebase for local and online.

Thanks for your help!


TXP Builders – finely-crafted code, design and txp

Offline

Board footer

Powered by FluxBB