- 🇺🇸United States tstermitz Colorado
Drupal Media Systemt and NGINX file upload problem, client_max_body_size
NGINX Error log Message: "client intended to send too large body"I recently rebuilt a new Digital Ocean server with LEMP, and installed Drupal 10 with success.
Media files uploads were sometimes failing without a user visible error message.
To fix this I had to add a client_max_body_size in TWO locations in my conf file. This directive is not suggested in any of the common Drupal NGINX conf examples.
Here is my working nginx.conf file main Server block:
server { listen 443 ssl; listen [::]:443 ssl; root /var/www/mydomain/web; server_name mydomain.com www.mydomain.com; index index.html index.htm index.nginx-debian.html index.php; client_max_body_size 100M; location / { try_files $uri $uri/ /index.php$is_args$args; } location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { log_not_found off; access_log off; allow all; } location @rewrite { rewrite ^/(.*)$ /index.php?q=$1; } location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { try_files $uri @rewrite; expires max; log_not_found off; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; client_max_body_size 100M; } # Deny Access to all of Wordpress Front End files location ~* ^/(/wp-admin*|/wp-cron*|/wp-config*) { rewrite ^ / permanent; } ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; # managed by Certbot }
- 🇺🇸United States dww
Marked 📌 Add a sample nginx configuration file Closed: duplicate duplicate, tagging this for Security improvements. +1 to doing this!
Thanks,
-Derek - 🇫🇷France andypost
That sounds like good material for help topic
Meantime there should be page like https://unit.nginx.org/howto/drupal/
So help topic can provide links to actual configuration maintained by upstream - 🇺🇸United States neclimdul Houston, TX
Maybe, but I think something in core is a better fit. Especially since the documentation could then be tied to specific versions instead of generalized documentation which ends up needing a sprinkling of "Do X for 7, Y for 8-10, Z for 10.1+" which ends up needing quite a bit of expertise to get an actual working config.
Another reason is 🐛 Stampedes and cold cache performance issues with css/js aggregation Fixed broke things and caused this to bubble back up. There wasn't a clear way to really communicate that within core and how sites should fix it. Honestly, unless you find the thread in slack, I'm not sure the possible fixes are even documented anywhere.
Additionally, I know the "official" nginx version has had problems in the past as well so would very much support providing something with best practices as captured by the Drupal community.
- 🇳🇱Netherlands gaele
Addition for multisite installation in a subdirectory:
https://samwaters.net/nginx-drupal-9-multisite-and-path-based-urls/In short:
location / { try_files $uri /index.php?$query_string; } #location /site1/ { # try_files $uri /site1/index.php?$query_string; #} #location /site2/ { # try_files $uri /site2/index.php?$query_string; #}
- 🇫🇷France O'Briat Nantes
There are some config from the default htaccess → that is not present in the patch, for example:
# Protect files and directories from prying eyes. <FilesMatch "\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config|yarn\.lock|package\.json)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$"> <IfModule mod_authz_core.c> Require all denied </IfModule> <IfModule !mod_authz_core.c> Order allow,deny </IfModule> </FilesMatch>
Since this patch is related to V11, all mention of Drupal 8 should be removed and the Change records Asset aggregation deprecations and additions, hook_js_alter()/hook_css_alter() changes → should be added.
- 🇫🇷France andypost
Not sure it doable as one file as fastcgi backend needs definition too, but in case of https://unit.nginx.org/howto/drupal/ less configuration required
-
+++ b/example.nginx @@ -0,0 +1,103 @@ + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + # Security note: If you're running a version of PHP older than the + # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini. + # See http://serverfault.com/q/627903/94922 for details. + include fastcgi_params;
the file is missing
-
+++ b/example.nginx @@ -0,0 +1,103 @@ + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
webp/avif should be added
-
- 🇫🇷France O'Briat Nantes
I try to port https://www.drupal.org/project/drupal/issues/3327115 🐛 .htaccess rules broken since yarn.lock got added Fixed as
location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ { deny all; return 404; }
You could test it with
find web -type f -regextype posix-egrep -regex '.*\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|.*\/(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config|yarn\.lock|package\.json)$|.*\/#.*#$|.*\.php(~|\.sw[op]|\.bak|\.orig|\.save)$' | sort | sed 's/web/https:\/\/myweb.site/' | xargs -I {} curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" {} | grep -v 404
- 🇳🇿New Zealand ericgsmith
@O'Briat I hit the same issue trying to update the regex with the change from #3327115 - your suggested config looks good to me - tested and now correctly seeing the expected 404 for composer.json, composer.lock, web.config, yarn.lock and package.json
- Assigned to ericgsmith
- 🇳🇿New Zealand ericgsmith
@O'Briat there looks to be a typo in the first diff - the regex is different from the test command - I was looking at the regex in your test command.
Your correct that the regex from the
.htaccess
file matches filenames and the nginx config matches URIs.My colleague @Rosk0 and I built out some test cases for that regex https://regexr.com/7o2av
I essentially just replaced with
^
with\/
so that anywhere the.htaccess
block was looking for the start of the string it now looks for/
I'm going to move the patch to an MR and address some of the comments above
- Issue was unassigned.
- Status changed to Needs review
about 1 year ago 1:54am 30 November 2023 - 🇳🇿New Zealand ericgsmith
Made the following changes to the branch:
Adjust regex - see commit message for explanation and test
Applied changes for asset aggregation from https://www.drupal.org/node/2888767#nginx-php-fpm →
Added additional file types suggested in #23
Updatedweb.config
,.htaccess
andexample.nginx
to block the newexample.nginx
file - 🇫🇷France O'Briat Nantes
@ericgsmith: Great, the
^
should have been remove from my regexp^(\.(?!well-known).*
, thanks for the review & fix.I did see it because it was already blocked by :
location ~ (^|/)\. { return 403; }
This block and the
location ~* ^/.well-known/
could be safely removed, no ?By the way, all the 403 should be switch to 404 for security obfuscation.
I wonder if this file should not be put in place by the scaffold process?
- 🇳🇿New Zealand john pitcairn
By the way, all the 403 should be switch to 404 for security obfuscation.
Only if Drupal does that in .htaccess for Apache as well. It's a misuse of the response code.
Sites are free to modify the defaults for their own purposes, if obfuscation is a requirement.
- First commit to issue fork.
- Status changed to Needs work
about 1 year ago 7:07am 1 December 2023 Hide patch as it's very outdated compared with the current recipe at https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/
As a note, you can also use this Nginx generator to help with getting an "A+" for security on SSL Labs.
- 🇩🇪Germany c-logemann Frankfurt/M, Germany
The old nginx.com recipe is gone. Here is a copy in wayback machine:
https://web.archive.org/web/20240511055421/https://www.nginx.com/resourc...It seems that it's now more important that we have a kind of official nginx example on d.o maybe not only in code.
- 🇫🇷France andypost
ATM there's 2 main forks
It looks like Nginx is moving towards Unit.
Here's the Nginx Unit recipe for Drupal: https://unit.nginx.org/howto/drupal/
- 🇸🇰Slovakia poker10
I think that Nginx Unit and Nginx + PHP-FPM are two different solutions and neither one is going away. If anything, we should probably still target classic Nginx + PHP-FPM config, which should be used by majority users these days (but I have not found relevant usage statistics for Nginx Unit).
Here's a post that compares the performance of PHP-FPM and Nginx Unit: Comparing PHP-FPM, NGINX Unit, and Laravel Octane
- 🇩🇪Germany c-logemann Frankfurt/M, Germany
The official wiki repo is still available:
https://github.com/nginxinc/nginx-wiki/blob/master/source/start/topics/r...Because there so many links to the nginx.com wiki I think it's a bad move to just shut it down.
Maybe they need a little bit help to configure a proper redirect.@solideogloria I don't know if unit fit's all the things I do with nginx config for now.
@andypost Especially angie seems to be very interesting. I planned to try it ASAP.
@solideogloria I don't know if unit fit's all the things I do with nginx config for now.
Me neither. I don't use it yet (still using PHP-FPM); I'm just looking at what's available.
- 🇫🇷France andypost
I'm using following config ATM for Nginx Unit
{ "access_log": "/dev/stdout", "listeners": { "*:80": { "pass": "routes/main" } }, "routes": { "main": [ { "match": { "uri": [ "!*/.well-known/*", "/vendor/*", "/core/profiles/demo_umami/modules/demo_umami_content/default_content/*", "*.engine", "*.inc", "*.install", "*.make", "*.module", "*.po", "*.profile", "*.sh", "*.theme", "*.tpl", "*.twig", "*.xtmpl", "*.yml", "*/.*", "*/Entries*", "*/Repository", "*/Root", "*/Tag", "*/Template", "*/composer.json", "*/composer.lock", "*/web.config", "*sql", "*.bak", "*.orig", "*.save", "*.swo", "*.swp", "*~" ] }, "action": { "return": 404 } }, { "match": { "uri": [ "/core/authorize.php", "/core/install.php", "/core/modules/statistics/statistics.php", "~^/core/modules/system/tests/https?\\.php", "/core/rebuild.php", "/update.php", "/update.php/*" ] }, "action": { "pass": "applications/drupal/direct" } }, { "match": { "uri": [ "!/index.php*", "*.php" ] }, "action": { "return": 404 } }, { "match": { "uri": [ "~^.*css_[a-zA-Z0-9-_]+\\.css(?:\\?.*)?$", "~^.*js_[a-zA-Z0-9-_]+\\.js(?:\\?.*)?$" ], "headers": [ { "Accept-Encoding": "*gzip*" } ] }, "action": { "pass": "routes/assets_gz" } }, { "action": { "share": "/var/www/html/web$uri", "fallback": { "pass": "applications/drupal/index" } } } ], "assets_gz": [ { "action": { "share": "/var/www/html/web${uri}.gz", "response_headers": { "Content-Encoding": "gzip" }, "fallback": { "pass": "routes/assets" } } } ], "assets": [ { "action": { "share": "/var/www/html/web${uri}", "fallback": { "pass": "applications/drupal/index" } } } ] }, "applications": { "drupal": { "type": "php", "stdout": "/dev/stdout", "stderr": "/dev/stderr", "processes": { "max": 4, "spare": 2, "idle_timeout": 120 }, "limits": { "timeout": 300, "requests": 1500 }, "options": { "admin": { "apc.serializer": "igbinary", "memory_limit": "1G", "opcache.jit_buffer_size": "20M" } }, "targets": { "direct": { "root": "/var/www/html/web/" }, "index": { "root": "/var/www/html/web/", "script": "index.php" } } } } }
- 🇸🇪Sweden fred6633
This code below breaks my site, if I enable aggregate css and javascript. It's a composer install with a web directory. Brave says too many redirects.
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|avif)$ { try_files $uri @rewrite; expires max; log_not_found off; }
If I replace "try_files $uri @rewrite;" with "try_files $uri /index.php?$query_string;" it works.
I also have not been able to get this code to work with images that Drupal has converted to webp. Drupal creates a file "filename.png.webp". Drupal also sets "?itok=suhCNess", so the total name is "filename.png.webp?itok=suhCNess" But is has no effect to change the regex to "webp.*". The expiration directive still does not work.
The approach that works for me in order to get the expires directive to work with converted webp images is described here: https://linux-audit.com/web/nginx-adding-expires-header-to-improve-caching/
The nginx.org recipe was outdated compared to Drupal's .htaccess file. It's probably a good thing that Nginx.org removed it.
This one looks better, at least when it comes to the "Protect files and directories from prying eyes" section:
https://github.com/uselagoon/lagoon-images/blob/main/images/nginx-drupal...
- 🇸🇪Sweden fred6633
Don't forget this :
https://www.drupal.org/project/drupal/issues/3034643 🐛 File not found web/update.php/selection Closed: outdated .
I had to add code under comment #15 in order to run update.php. - 🇺🇸United States greggles Denver, Colorado, USA
If #2868079: Add a default Content-Security-Policy-header for svg files → happens then that should get rolled into these recipes as well.
- 🇬🇧United Kingdom longwave UK
Now we have GitLab CI, we have the possibility of running some tests on nginx, which would be good to ensure we match the protections we provide for Apache.
- 🇫🇷France andypost
Curious how we can create image containing both nginx and apache
- 🇬🇧United Kingdom longwave UK
@andypost install both in the container, run Apache by default, but in a specific CI job we kill it and run nginx instead before starting tests?
- 🇫🇷France andypost
Yes, there's server-setup.sh script which can be improved, so only a backend question remains - fpm or what?!
- 🇺🇸United States greggles Denver, Colorado, USA
The issue summary says:
Nginx is one of the most popular web servers on which Drupal is run.
Does anyone have data about the level of popularity?