Grav and dynamic content with Apache

Grav https://getgrav.org is a solid CMS for static pages. But what to do if you have dynamic content ... lot's of it ...?

Introduction

What am I doing? It all started with wanting to move a website from A to B. In the process I wanted to switch from my own very basic and rough to handle CMS solution to a ready to use CMS. I needed something very lightweight, because I wanted to move my website on a tiny server with very minimal specs. After a bit of reading, my decision was to use Grav. I decided to do some testing with it before starting the actual website moving, because it was going to be quite a change. I decided to do the testing on my home server, for being able to learn every part of the setup process. The arising problem was, that I couldn't simply throw away the old content on my home server. This server is my setup for testing different things. It is also the interface to check some data from my home automation. Overall it has lots of dynamic data to display and a few things that I like and/or need to keep.

Grav is great for static content. It precompiles the pages written in Markdown and Twig. It offers a nice and clean structure for handling your content and themes based on directories and files. The generated and cached pages load fast. It is possible to generate dynamic content with Grav, but of course that will cost compile time on every page call and neither I nor other users like to wait. So it would be great to load the static content quickly from Grav. As for the dynamic content, it's more efficient to load it asynchronously without accessing Grav. If possible I'd like to use the old stuff, which is usually PHP generated JSON data, called asynchronously from JavaScript routines. So, how to get this running?

Setup the website directory

I wanted Grav and the dynamic pages in seperate directories right next to each other to have everything easily accessible. So I planned the layout like this:

/directory/to/the/web/content/
  |
  + gravFolder/
  |  |
  |  + // all the stuff from Grav
  |
  + resourcesFolder/
     |
     + // all the dynamic resources

I copied the downloaded Grav CMS content into the gravFolder directory. All the other PHP code (getting data from the database, event generators, ...) went into the resourcesFolder folder.

Configure Apache

Apache needed to be configured

  1. to know, where Grav is and to use it as default location and
  2. to know, where the dynamic content is and to allow access to it.

I also wanted Grav to generally handle errors (like "404 file not found"), even if they are happening when accessing the resources directory. That way everything looks homogeneous and consistent.

All of that can be configured in the httpd.conf file. I found it more useful to handle the errors in the error configuration, so I touched that too. After setting up PHP in the httpd.conf, I went on to make the changes for Grav.

!!! **Hint**: for changes in the config files of Apache to take effect, the Apache server needs to be restarted.

Grav content as root directory

I simply followed the instructions on https://learn.getgrav.org/, except for one point. Grav needs rights to change the content in its own directory, so the instruction is to set AllowOverride All. I only want to do that for the gravFolder, so I put an extra entry in the configuration:

<Directory "/directory/to/the/web/content/gravFolder">
    AllowOverride All
</Directory>

For everything else, Apache will use the default settings.

Additionally I only allow access to the admin section of Grav from the local network, just as a little additional security layer. The following code snippet will redirect to Grav's error page when /admin is accessed from outside.

<If "! -R '192.168.0'">
    Redirect /admin /error
</If>

For those who want to try rewriting the URI internally with RewriteRules (so that the address in the calling browser doesn't change): I gave up on this after a day of trying. The rules in Grav's .htaccess file make this very hard to achieve. They are important security rules though, so be careful with changing them. Feel free to send me an email, if you find a solution!

Access dynamic content

I use an Alias statement in order to be able to call our dynamic content from outside:

Alias "/resources" "/directory/to/the/web/content/resourcesFolder"

Now I could request dynamic content pages via the url http://myexample.com/resources/*, where * is the path and name of your content page in the resourcesFolder.

!!! **Hint**: The order in httpd.conf matters! The Alias statement needs to be before the directory entry of the gravFolder. Otherwise Apache and Grav will try to find the resources in the gravFolder, which will fail unless you have an item with the same name in Grav.

Error handling

Apache is handling it's errors in the configuration file httpd-multilang-errordoc.conf, which can be found inside the extra folder in the Apache's conf directory. The different errors are already listed there, so I decided to change it, after making a backup of it.

Since my goal was to display the error message from Grav, I needed to do two things:

  1. Remove the link to Apache's error page
  2. Make the messages point to Grav's error page

I achieved the first by putting a comment infront of the Alias directive, that points to Apache's error directory:

#Alias /error/ "/usr/share/httpd/error/"

Secondly, I changed the paths behind the ErrorDocument directives to point to the standard error page, generated by Grav:

ErrorDocument 400 /error
ErrorDocument 401 /error
ErrorDocument 403 /error
...
ErrorDocument 503 /error
ErrorDocument 506 /error

Done! Now all error messages are the ones from Grav.

Edit (stuff I found out later)

  • Grav is actually not allowing the placement of PHP code in the markdown pages. It prints out, that it's forbidden because using PHP is unsafe, or something similar.
  • When I moved back from a virtual server to webhosting, I couldn't set up Apache anymore, like I wanted to. It was that weekend, that I found out this: It's possible to simply place additional directories (like a resource folder) in the root folder of the Grav setup. Grav seems to ignore it.