0

From an old project, I got this .htaccess file, which currently handle the apache rewrite rules:

<IfModule mod_rewrite.c>
    RewriteEngine On
    # Folders / files to exclude from rewrite divided by Pipe goes here:
    RewriteRule (^|/)install(/|$) - [L,NC]
    RewriteRule (^|/)web(/|$) - [L,NC]

    # turn empty requests into requests for "index.php",
    # keeping the query string intact
    RewriteRule ^$ index.php [QSA]

    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !favicon.ico$
    RewriteRule ^(.+)$ index.php [QSA,L]

    RewriteRule ^(.+)$ index.php [QSA,L]
</IfModule>

What the above snippet basically does, is that it rewrites all requests to index.php by keeping the query part string intact and stop after the first match, unless the file or folder exists.

So, in conclusion, by consulting the Nginx docs and by adding some additional security (permit access to sensible folders and files) and performance-related stuff (enable gzip or brotli output compression where it's possible [depending on the client, which one it is able to process]), I got this:

server {
    listen 80;
    listen [::]:80;
    server_name subdomain.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name tdc.wunner-software.de;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Add headers to serve security-related headers
    # Before enabling Strict-Transport-Security headers please read into this
    # topic first.
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
    #
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Download-Options "noopen" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "none" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    # Path to the root of your installation
    root /srv/www/vhosts/example.com/subdomain.example.com;

    index index.php index.html;

    charset utf-8;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # set max upload size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    brotli on;
    brotli_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    # Adding the cache control header for js, css and map files
    # Make sure it is BELOW the PHP block
    location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463";
        # Add headers to serve security related headers (It is intended to
        # have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into
        # this topic first.
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;

        # Optional: Don't log access to assets
        access_log off;
    }

    location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$ {
        try_files $uri /index.php$request_uri;
        # Optional: Don't log access to other assets
        access_log off;
    }

    location ~ ^/vendor/.*$ {
        deny all;
    }

    location ~ ^\.htaccess$ {
        deny all;
    }

    #rewrite ^(.*)/$ index.php$is_args$args break;

    location ~ /(.*)$ {
        index index.php;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi.conf;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param HTTPS $https;
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300s;
        client_body_buffer_size 128k;
        fastcgi_pass php-handler;
        http2_push_preload on;
    }

    access_log /var/log/nginx/subdomain_example_com_ccess.log;
    error_log /var/log/nginx/subdomain_example_com_error.log;
}

The new Nginx configuration above downloads the PHP file instead of bypassing it to the FPM handler. I have found this and this SO questions, but it doesn't seem to do the trick for me. Also, as mentioned in Nginx documentation, if is evil and we should avoid using it unless we are 100 percent sure what's going on. So, for my configuration, I avoided using it. Mainly because regex syntax also works with location and is way more securer in that context than if. I mean, as far as I know. Please correct me, if I'm wrong.

The main nginx.conf file is dead simple right now:

user  nginx;
worker_processes  1;

# load_module lib64/nginx/modules/ngx_http_fancyindex_module.so;
# load_module lib64/nginx/modules/ngx_http_geoip_module.so;
# load_module lib64/nginx/modules/ngx_http_headers_more_filter_module.so;
# load_module lib64/nginx/modules/ngx_http_image_filter_module.so;
# load_module lib64/nginx/modules/ngx_http_perl_module.so;
# load_module lib64/nginx/modules/ngx_http_xslt_filter_module.so;
# load_module lib64/nginx/modules/ngx_mail_module.so;
# load_module lib64/nginx/modules/ngx_rtmp_module.so;
# load_module lib64/nginx/modules/ngx_stream_geoip_module.so;
# load_module lib64/nginx/modules/ngx_stream_module.so;

error_log  /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
error_log  /var/log/nginx/error.log  info;

#pid        /run/nginx.pid;

events {
    worker_connections  1024;
    use epoll;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;

    include conf.d/*.conf;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  /var/log/nginx/host.access.log  main;

        location / {
            root   /srv/www/htdocs/;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /srv/www/htdocs/;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
            root           /srv/www/htdocs/;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            include        fastcgi_params;
        }

        # deny access to .htaccess files if Apache's document root
        # concurs with Nginx's one
        #
        location ~ /\.ht {
            deny  all;
        }
    }


    # another virtual host using a mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   /srv/www/htdocs/;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
    
    #    Allow TLS version 1.2 only, which is a recommended default these days
    #    by international information security standards.
    #    ssl_protocols        TLSv1.2;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   /srv/www/htdocs/;
    #        index  index.html index.htm;
    #    }
    #}

    include vhosts.d/*.conf;
}

The file above is mainly the openSUSE distributor shipped file, with a few changes.

And this is my base.conf file, which defines the FPM proxy handler for nginx (in order to don't repeat yourself; it's neccessarily needed anyways):

upstream php-handler {
    server 127.0.0.1:9000;
    #server unix:/var/run/php/php7.2-fpm.sock;
}

I'd appreciate any help with that issue.

bob
  • 2,674
  • 1
  • 29
  • 46
alpham8
  • 1,314
  • 2
  • 14
  • 32

1 Answers1

0

What the above snippet basically does

You might take some time to think about the less obvious things these rules do.

The new Nginx configuration above downloads the PHP file instead of bypassing it to the FPM handler

Because all your references to php scripts are being intercepted by location ~ \.(?:css|js|woff2?|svg|gif|map)$ which does not hand off work to the PHP FPM. Understanding the precedence in which nginx considers location{} blocks is critical to implementing a successful configuration.

Your try_files directive seems to apply to the server as a whole but you have put it in a location block. It should be in the server block such that it is applied before nginx tries to match a location block.

Using a virtual (or "named") location block provides a means of handling the case where the default option has a handler (like PHP). See the example for Drupal/Fastcgi in the documentation for try_files.

Your config should look something like

server {
...
try_files $uri @phpfpm;

location ~ \.(css|js|woff2?|svg|gif|map)$ {
        add_header Cache-Control "public, max-age=15778463";
        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
        access_log off;
}
location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi.conf;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param HTTPS $https;
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300s;
        client_body_buffer_size 128k;
        fastcgi_pass php-handler;
        http2_push_preload on;
}
location @phpfpm {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi.conf;
        fastcgi_param HTTP_PROXY "";
        fastcgi_param HTTPS $https;
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300s;
        client_body_buffer_size 128k;
        fastcgi_pass php-handler;
        http2_push_preload on;
}

Although you should really wrap-up all that PHP config in a separate file. Also, you seem somewhat concerned about getting good performance but are using a TCP socket connection to localhost rather than a filesystem socket - which impacts performance and capacity.

symcbean
  • 47,736
  • 6
  • 59
  • 94
  • Thank you, but it doesn't matter what I'm trying, I'm getting always HTTP error 500 without any log entry. May you please fix your answer? – alpham8 Feb 27 '21 at 15:10
  • No - because I don't have access to your computer, your configuration, your logs and package management. – symcbean Feb 28 '21 at 20:56