403 in media folder for plugin link

I have a weird issue with the media folder and the michnhokn/kirby3-cookie-banner plugin by @michnhokn (but think it’s not the plugins fault). I use Kirby and the plugins with composer, no special config except that.

On my local machine everything works fine but once I go on the server I get the following error for all assets of it:

[Error] Failed to load resource: the server responded with a status of 403 () (cookie-modal.js, line 0)

I deleted the media folder already but it recreated and had the same problem again.

Does anyone here have an idea what to try/where to look at?
Thanks for any help/idea.

Is the symlink in the media folder actually created? Where does it point to? Is the location of the original file readable, i.e. what are the file permissions?

Thanks already for the suggestions, I checked it now.

Locally the alias exists and works, on remote it’s a bit hard to debug since I don’t have ssh access but only sftp. But the original file in site/plugin/… exists and has correct permissions (644) and the ones in media folder seem to have the same but they’re created automatically so don’t think I can influence that anyways.

Is that the only plugin you use that has frontend styles/scripts?

I’m using the same plugin in a project on shared hosting without any issues, so I assume the plugin is not at fault and if the files exist and the symlinks are created as expected, then it could either be a permission issue, a caching problem (have you tried to clear the browser cache?), or maybe some sort of browser add-on interfering (although in the latter case, I would expect issues also locally).

I have the same error. 403 on cookie-modal.js
Symlink created and points to file.
Local is everything fine. Hosting on Mittwald

I have the same issue on Mittwald.
How did you solve this?

The absolut media-folder-root-path has to be configured in the index.php to reflect mittwalds folder-structure /home/www/pxxxxxx/html/.

Delete the media-folder, so that kirby creates new symlinks for the assets on pageload.

Well, this works exactly one time, when the media folder is deleted and then the files get cached.
The symlinks don´t point to the desired absolute after regeneration.

I’ve got the same issue, did you find the solution ?

Hi Adrien,

if i remember correctly it did not work globally, but when you set the mittwald-path path in every plugins/my-plugin/index.php by hand it does.

let me know, if this works for you, otherwise i´ll can have a look at my concrete settings.


Hi pesto,

Thank you for your help. Can you be more precise ? My project is hosted on OVH (french hoster).


Does anyone have a generic solution for this issue?
Since I have a deployment using symlinks for my project, manually updating the asset file symlinks in all plugin folders is not an option for me.
Is it possible to intersect the /media/plugins/ route and to serve the file contents using a plugin?
My first attempt an doing this, did exacly nothing. It seems that Kirby is generating the media files before any execution of plugin routes. Also intercepting the 403 and try to do some stuff there did not work so far.

Why is Adrieng’s post marked as resolved?

If anybody needs a quick fix, i just added some lines in my index.php to intercept the symlink creation from Kirby.

// ... this should go to after instatiating kirby in your index.php or route.php

// Check if request uri starts with media/plugins
if (strpos($_SERVER['REQUEST_URI'], '/media/plugins') === 0) {
    // Loop through all plugins and check if the plugin has assets
    foreach ($kirby->plugins() as $plugin) {
        if (is_dir($plugin->root() . '/assets')) {
            $prefix = 'MISSING SERVER PREFIX FOR SYMLINK';

            foreach($plugin->assets() as $asset) {
                $path = $prefix . $plugin->root() . '/assets/' . $asset->path();
                $target = $prefix . $kirby->root('media') . '/plugins/' . $plugin->name() . '/' . $asset->path();
                if (!is_link($target)) {
                    F::link($path, $target, 'symlink');

echo $kirby->render();

This should only be run once. After the symlinks are created, the real media file will be served by the server.
I hope it helps sombody