On serving a site on `http://mydomain/2020/` with Grav in `2020/grav`

Sorry, this is a long post… You won’t need a bedtime story tonight!

Some time ago I’ve decided that Grav should be kept in its own directory. This means that my normal setup looks like this:

  • /.htaccess
  • /grav/

Where /.htacess typically is:

RewriteEngine On

RewriteCond %{REQUEST_URI} !^/grav/
RewriteRule ^$ /grav [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /grav/$1 [L,QSA]

And that works fine.

Now, I need to setup a new site that is not at the document root but in /2020, which will mean that Grav will be in /2020/grav.

I’ve tried to create the /2020/.htaccess based on the above working example, but did not get to something that was usable.

On Stackoverflow I got this proposal that should work:

RewriteEngine On

RewriteRule ^$ grav/ [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(?!grav/)(.+)$ grav/$1 [L,NC]

It does not.

To be sure that this .htaccess file is correct, I’ve replaced the full content of the 2020/grav/ directory with the following index.php file:

<?php

echo("<p>Grav would be processing your request</p>\n");

echo('<pre>'.print_r($_REQUEST['path'], 1)."</pre>\n");

and created a new /2020/grav/.htaccess file:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?path=$1 [NC,L,QSA]

Both requests /2020/ and /2020/hello show what you would expect (the welcome message and – for the second request-- also the “hello” message).

Now, let’s get back to Grav.

If I put the Grav files into /2020/ everything works perfectly!

But If I put them in 2020/grav/ (where I really think they belong!) then I cannot get a clean url.

What’s happens when I call http://mydomain/2020/?
The url in the browser bar changes to 'http://mydomain/2020/grav/2020and I get a "Grav" 404 error. If I click on the "Typography" link in the menu bar, I indeed get the content ofhttp://ww.xox.ch/2020/grav/typography`.
Everything would be perfect, I there was not that “grav” in the url!

If I go to http://mydomain/2020/typography`, of course, I get a Grav 404 (to be clear: it’s not an Apache 404, it’s the one generated by Grav!)

Now, an interesting fact: if I use
curl -LI "http://mydomain/2020/"
(which does follow redirections), I get the following output:

HTTP/1.1 302 Found
Date: Wed, 04 Sep 2019 19:07:50 GMT
Server: Apache/2.4.41 (Debian)
Set-Cookie: grav-site-fa41bd3=95284a5lvrh3n78ru0dirakd2l; expires=Wed, 04-Sep-2019 19:37:50 GMT; Max-Age=1800; path=/2020/grav/;
domain=mydomain; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Set-Cookie: grav-site-fa41bd3=95284a5lvrh3n78ru0dirakd2l; expires=Wed, 04-Sep-2019 19:37:50 GMT; Max-Age=1800; path=/2020/grav/;
domain=mydomain; HttpOnly
Location: /2020/grav/2020
Content-Type: text/html; charset=UTF-8

HTTP/1.1 404 Not Found
Date: Wed, 04 Sep 2019 19:07:50 GMT
Server: Apache/2.4.41 (Debian)
Set-Cookie: grav-site-fa41bd3=3l7337mkjv1t20ffdlfl4ee1ok; expires=Wed, 04-Sep-2019 19:37:50 GMT; Max-Age=1800; path=/2020/grav/;
domain=mydomain; HttpOnly
Expires: Wed, 11 Sep 2019 19:07:50 GMT
Cache-Control: max-age=604800
Pragma: no-cache
Set-Cookie: grav-site-fa41bd3=3l7337mkjv1t20ffdlfl4ee1ok; expires=Wed, 04-Sep-2019 19:37:50 GMT; Max-Age=1800; path=/2020/grav/;
domain=mydomain; HttpOnly
Content-Encoding: none
Content-Length: 10996
Connection: close
Content-Type: text/html;charset=UTF-8

First, I get a “found” answer, but finally It’s a 404.

As a further check, I’ve put some debugging output in:
grav/system/src/Grav/Common/Grav.php Grav::redirect()
and, indeed, Grav sends a request to redirect the request /2020/ to /2020/grav/2020.

The problem seems to be hidden somewhere deep in the Grav code…

So, any idea what I can try to get a nice Grav site that sits at http://mysite/2020 ?
Or, even better, what do I have to change to get there?

For posterity:

  1. Create /webroot/2020/grav.
  2. Fill it with candy, ie. a full Grav-installation.
  3. Add a AddType application/x-httpd-ea-php72 .php handler to /webroot/2020/grav/.htaccess because the server defaults to outdated PHP.
  4. Edit / webroot/2020/grav/user/config/system.yaml and add this:
custom_base_url: 'http://domain.tld/2020'
session:
  path: /2020
  1. Create /webroot/2020/.htaccess with:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/2020/grav/
RewriteRule ^(.*)$ /2020/grav/$1
  1. Profit!

A recipe from 2016 actually exists: https://github.com/getgrav/grav/pull/896, even some nice clues in an old forum post: Grav in subfolder without site visitors ever knowing.

“For posperity” because Ole helped me find this solution in the chat.

Thanks Ole!