Panel sends GET instead of POST for changes/save on shared hosting — only on production server, works locally

Kirby version: 5.3.0
PHP version (production): 8.2.30 (updated to 8.5 during troubleshooting — same issue)
PHP version (local): 8.5.3 (Docker, shinsenter/kirby:latest)
Server: Apache/2.4.66 (Unix) on Strato shared hosting
Browser: Tested in both Safari 26.2 and Chrome — same behavior in both

Description

When editing any page in the Panel on my production server (Strato shared hosting), any content change — even typing a single character in a text field — triggers the following error:

{
    "status": "error",
    "message": "No route found for path: \"pages/projects+porject-name/changes/save\" and request method: \"GET\"",
    "code": 404,
    "exception": "Exception",
    "key": null,
    "file": "kirby/src/Http/Router.php",
    "line": 191
}

The issue does not occur locally using Docker (shinsenter/kirby:latest). Only on the Strato shared hosting server.

Root cause observation

Looking at the browser Network tab, the Panel sends a GET request to the changes/save endpoint instead of a POST:

Failing request (affected pages):

URL: https://example.com/api/pages/projects+some-project/changes/save
Request Method: GET
Status Code: 404 Not Found
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate

Working request (other pages on the same site):

URL: https://example.com/api/pages/projects+polygarden/changes/save
Request Method: POST
Status Code: 200 OK

The key detail: the failing request has Sec-Fetch-Dest: document and Sec-Fetch-Mode: navigate, meaning the browser is performing a full page navigation rather than an AJAX/fetch POST call. This suggests the original POST request is being redirected (301/302) by the server, and the browser follows the redirect as a GET — which then fails because changes/save is only registered as a POST route.

What I checked

  • .htaccess: Standard Kirby .htaccess is in place, unmodified. No trailing slash redirect, no HTTP-to-HTTPS rewrite. I even tested with the .htaccess missing entirely — same error.

  • File permissions: The affected project folders had _changes directories with 750 permissions. Deleting the _changes folders and re-uploading the content resolved the issue temporarily, but it eventually came back.

  • Plugins: I have pechente/kirby-password-guard installed. I set enabled: false in the config and the issue persisted, ruling out the plugin as the cause.

  • PHP version: Started with PHP 8.2.30, upgraded to 8.5 on the server — no change.

  • Browsers: Reproduced in both Safari and Chrome.

  • Local environment: Cannot reproduce locally in Docker at all. Same codebase, same Kirby version.

Pattern

  • The issue only occurs on the Strato shared hosting server, never locally

  • Initially it only affected specific projects (those that had existing _changes folders with restrictive permissions), but eventually affected all projects

  • Deleting _changes folders from the server temporarily fixes it until Kirby recreates them

  • Pages that have never been edited (no _changes folder yet) work fine until the first _changes folder is created

  • The _changes folders created by Kirby on the server get 750 permissions, which may be too restrictive for the web server process

Suspicion

I believe the issue is related to how the _changes directory is created on shared hosting. When Kirby creates the _changes folder, it gets 750 permissions on my Strato server. If the Apache/PHP process cannot write to this directory on subsequent requests (e.g., due to a user/group mismatch), the write operation fails, and the Panel’s error handling causes a browser navigation (GET) instead of gracefully handling the error in the AJAX response.

The fact that freshly uploaded content (without _changes folders) works fine, and then breaks once Kirby creates the _changes directory, supports this theory.

Steps to reproduce

  1. Deploy a Kirby 5.3.0 site to Strato shared hosting (Apache 2.4.66)

  2. Create/edit a page in the Panel

  3. Wait for Kirby to create the _changes directory

  4. Edit the same page again — type a single character in any text field

  5. Observe the 404 error in the browser console

Expected behavior

The Panel should send a POST request to changes/save and receive a 200 response.

Actual behavior

The Panel sends a GET request (browser navigation) to changes/save, which returns a 404 because no GET route exists for that path.

Workaround

Manually deleting _changes folders from the server and setting directory permissions to 755 or 775 temporarily resolves the issue until Kirby recreates them.

Ok, I cannot test on Strato shared hosting, they want my payment details before I can create a test account.

The _changes folder should actually be created with 755 permissions and the files inside it with 644. This is what a umask of 022 does. Could you check the umask on your server, add the following in a template:

echo 'umask: ' . decoct(umask());

However, I wonder if this is really the issue here, because temporarily forcing the server to use a umask of 027 which then produces folders with 750 and files with 640 does not cause the issues you describe.

If you observe the network tab during the changes, does it redirect the post request?