From 8aef6e5b5acbfe2b41628268b00f5ebf3ce7c098 Mon Sep 17 00:00:00 2001 From: ignacionelson Date: Fri, 11 Sep 2026 12:45:26 -0300 Subject: [PATCH] Tell an Apache install what it needs, and where its 500 is written MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by @Zodiac1978 in #1778, on IONOS. Step 6 was nginx and only nginx, while "What you need" says Apache is fine. It is fine, but not without being told two things — document root at public/, and AllowOverride All with mod_rewrite on, or the .htaccess we ship does nothing and every address but the home page is a 404. There is now an Apache vhost beside the nginx one. The 500 in the report is its own troubleshooting entry, because the entry we had sends people to storage/logs/ and for this class of failure that directory is empty — Apache never reached PHP, so ProjectSend had nothing to write, and an empty log reads as a dead end rather than as the clue it is. The error is in Apache's log. Two causes cover nearly all of them: Options refused by AllowOverride, and the internal-redirect loop this reporter hit, where Apache cannot derive the per-directory base and the front-controller rule rewrites to a path that is not there, repeatedly. public/.htaccess now carries a commented-out RewriteBase with the explanation next to it, which is where somebody debugging a 500 is already looking. Only RewriteBase is documented, not the report's second change — making the substitution absolute (`/index.php`). With the base set correctly the relative form resolves to the same place, and the absolute one would send a subdirectory install to the domain root's index.php instead. The trap underneath all of this is worth its own paragraph, and nothing said it before: update.sh merge-copies the release over the install, so an edit to public/.htaccess is reverted on the next update and the site 500s again. Put the directives in the vhost if it is yours to edit, since an update cannot reach there — and on shared hosting, where it is not, keep a note. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01CNFU55Tkq6MuEQ73nbbBRx --- INSTALL.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- public/.htaccess | 9 ++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index b39e6d10..68d9e50b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -324,8 +324,9 @@ like your logo reachable from the web. ## Step 6 — Point your web server at it -A complete nginx server block. Change `server_name`, and change `/var/www/projectsend` to wherever -you unpacked the files (there are **three** places, including one inside `/protected-files/`): +A complete nginx server block below; [Apache is further down](#if-you-are-using-apache). Change +`server_name`, and change `/var/www/projectsend` to wherever you unpacked the files (there are +**three** places, including one inside `/protected-files/`): ```nginx server { @@ -374,6 +375,38 @@ server { } ``` +### If you are using Apache + +Two things matter, and both are easy to get wrong: + +- **The document root is the `public/` directory**, not the directory you unpacked into. Everything + above `public/` — your `.env`, your uploaded files, the application code — has to stay out of + reach of any URL. +- **`AllowOverride All`, and `mod_rewrite` enabled** (`sudo a2enmod rewrite`). ProjectSend ships a + `public/.htaccess` that sends every address to the front controller. If Apache is told to ignore + it, every page except the home page is a 404. + +```apache + + ServerName files.example.com + DocumentRoot /var/www/projectsend/public + + + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/projectsend-error.log + CustomLog ${APACHE_LOG_DIR}/projectsend-access.log combined + +``` + +Downloads work as they are: PHP sends the bytes. If that becomes a capacity problem, `mod_xsendfile` +hands the job to Apache — see [How downloads are sent](#how-downloads-are-sent). + +On shared hosting you usually cannot edit any of this, and `public/.htaccess` is all you have. If +the site returns a 500 on every page, see [When something goes wrong](#when-something-goes-wrong). + Then check your PHP settings. Large uploads are sent in 20 MB pieces, so PHP never has to handle a whole 5 GB file at once — but the pieces still need room. In your `php.ini`: @@ -581,6 +614,28 @@ names the exact command to run; do that, then reload. Look in `storage/logs/` — open the newest file, the real error is at the bottom. Nine times out of ten it is folder permissions (step 4) or a wrong database password (step 3). +**Every page is a 500, and `storage/logs/` is empty.** +The empty log is the answer, not a dead end: nothing reached PHP, so ProjectSend had nothing to +write. The error is your web server's, and it is in your web server's log — on Apache +`/var/log/apache2/error.log`, or wherever your host puts it. On Apache two causes account for +almost all of these, and both are about `public/.htaccess`: + +- **`Options not allowed here`.** The file starts by turning off directory listings and content + negotiation, and your `AllowOverride` does not permit that. Allow it (`AllowOverride All`), or + delete the `Options` line — it is hardening, not a requirement. +- **`Request exceeded the limit of 10 internal redirects`.** Apache cannot work out which directory + the file is serving, so the rule that sends every address to `index.php` rewrites to a path that + does not exist, and tries again. Uncomment the `RewriteBase` line in `public/.htaccess` and set it + to the path ProjectSend is served from — `/` at the domain root, `/projectsend` in a subdirectory. + Reported on IONOS by [@Zodiac1978](https://github.com/Zodiac1978) in + [#1778](https://github.com/projectsend/projectsend/issues/1778). + +**If you edit `public/.htaccess`, write down what you changed.** Updating replaces every file the +release ships, that one included, so a change that made your site work will be gone after the next +update and the 500 will come back. If the Apache configuration is yours to edit, put the directives +in a `` block in the vhost instead: they do the same job there, and no update can touch +them. On shared hosting, where it is not yours, keep the note and re-apply it. + **"Please provide a valid cache path" or "failed to open stream".** `storage/` or `bootstrap/cache/` is not writable by the web server user. Step 4. diff --git a/public/.htaccess b/public/.htaccess index 3aec5e27..29826b69 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -14,6 +14,15 @@ RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] + # If every page returns a 500 and storage/logs/ is empty, Apache could + # not work out which directory this file is serving, and the rule below + # rewrites to a path that does not exist — over and over, until Apache + # gives up. Some shared hosts need to be told. Uncomment the line and + # set it to the path ProjectSend is served from: "/" at the domain root, + # "/projectsend" in a subdirectory of it. + # + # RewriteBase / + # Send Requests To Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f