Problem/Motivation

nginx is even more popular than Apache: https://w3techs.com/technologies/overview/web_server

We ship with .htaccess for Apache and web.config for IIS. We do not provide any sample configuration for nginx.

There is a recipe in the nginx wiki at https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/ but it likely does not match our current set of regular expressions, header rules, etc, that we ship in .htaccess and web.config.

It is likely that we cannot provide a drop-in configuration file as the exact location of the php-fpm upstream depends on how php-fpm is installed, but we could provide a commented sample file that matches our current best practices for security.

Steps to reproduce

Proposed resolution

Add a sample configuration file for nginx that is equivalent to the rules in .htaccess/web.config.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3336659

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

    Comments

    longwave created an issue. See original summary.

    effulgentsia’s picture

    +1. There's also some prior art in https://github.com/uselagoon/lagoon-images/blob/main/images/nginx-drupal... and maybe other places on the internet as well. It would be great to have an official canonical version in core.

    tstermitz’s picture

    Drupal Media Sysetm and NGINX file upload problem, client_max_body_size

    Error Message: "client intended to send too large body"

    I recently rebuilt a new Digital Ocean server with LEMP, and installed Drupal 10 with success. (Relatively smooth sailing, I must say!).

    I had to work through a few issues that would be easier if there were some good Drupal-successful NGINX Conf files. I uses a couple of tutorials, and examples including the Drupal recipe listed above in the initial issue:

    https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on...
    https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-wit...

    A difficult issue I had was dealing with Media upload of files which failed silently. I was only able to identify the problem in the nginx error logs. Even with php.info max_file_size set to 100M, NGINX needs a "client_max_body_size: 100M;" directive. This needed to be placed in both the main Server block, and the PHP Location block.

    The requirement of adding the client_max_body_size directive is not very well documented as I discovered after searching with many google attempts.

    For the record here is my NGINX main server block:

    server {
    		listen 443 ssl;
    		listen [::]:443 ssl;
    
    		root /var/www/mydomain/web;
    
    		#server_name _;
    		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
    
    }
    
    server {
        if ($host = www.mydomain.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
        if ($host = mydomain.com) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
        listen 80;
        listen [::]:80;
        server_name mydomain.com www.mydomain.com;
        return 302 https://$server_name$request_uri;
    }
    dww’s picture

    Great idea, but let's continue in the earlier issue where this is proposed. Thanks!

    fred6633’s picture

    Thanks!
    Do you have a code to get expiration to work with images that Drupal converts to webp, eg "filename.png.webp"? Below does not work.

    location ~* \.(css|gif|ico|jpeg|jpg|js|png|webp)$ {
            try_files $uri @rewrite;
            expires 1y;
            log_not_found off;
        }
    
    fred6633’s picture

    Below code breaks my site, if I enable aggregate css and javascript.

    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
            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.

    Here is my code.

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    try_files $uri /index.php?$query_string;
     expires 1y;
    log_not_found off;
    add_header Cache-Control "private,must-revalidate";
    }

    Btw, I am an amateur when it comes to codes!