PHP-FPM – Fix Pages Loading Only After Restart

nginxperformancePHPphp-fpmwindows-subsystem-for-linux

So here's more context in a GIF

enter image description here

Notice how pages do not load on link clicks, but as soon as you run service php7.2-fpm restart it instantly loads the page and like that with every page.

What is causing this?

I'm on WSL2 Ubuntu 18.04, Nginx, PHP7, MariaDB, the website is fresh install of Drupal 8, this is a localhost.

The slow pages actually do load if you don't restart php-fpm, but only after 1-2 minutes of waiting.

nginx.conf (default)

    user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}


#mail {
#   # See sample authentication script at:
#   # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# 
#   # auth_http localhost/auth.php;
#   # pop3_capabilities "TOP" "USER";
#   # imap_capabilities "IMAP4rev1" "UIDPLUS";
# 
#   server {
#       listen     localhost:110;
#       protocol   pop3;
#       proxy      on;
#   }
# 
#   server {
#       listen     localhost:143;
#       protocol   imap;
#       proxy      on;
#   }
#}

Drupal Nginx config

server {
        listen 8080;
        listen [::]:8080;
        server_name d8.local;
    root /var/www/d8.local/web;

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

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

    # Very rarely should these ever be accessed outside of your lan
    location ~* \.(txt|log|twig|yml)$ {
        allow 192.168.0.0/16;
        deny all;
    }

    location ~ \..*/.*\.php$ {
        return 403;
    }

    location ~ ^/sites/.*/private/ {
        return 403;
    }

    # Block access to scripts in site files directory
    location ~ ^/sites/[^/]+/files/.*\.php$ {
        deny all;
    }

    # Allow "Well-Known URIs" as per RFC 5785
    location ~* ^/.well-known/ {
        allow all;
    }

    # Block access to "hidden" files and directories whose names begin with a
    # period. This includes directories used by version control systems such
    # as Subversion or Git to store control files.
    location ~ (^|/)\. {
        return 403;
    }

### advagg_css and advagg_js support
   #location ~* files/advagg_(?:css|js)/ {
    location ~* /sites/default/files/js/.*\.js$ {
        access_log off;
        expires 1w;
        add_header ETag "";
        add_header Cache-Control "max-age=2628000, no-transform, public";
        try_files $uri $uri/ @rewrite;
    }

    location ~* /sites/default/files/css/.*\.css$ {
        access_log off;
        expires 1w;
        add_header ETag "";
        add_header Cache-Control "max-age=2628000, no-transform, public";
        try_files $uri $uri/ @rewrite;
    }

    #location ~* \.(?:css|js|ico|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
    #    expires 1w;
    #    add_header ETag "";
    #    add_header Cache-Control "max-age=2628000, no-transform, public";
    #}

    location / {
        # try_files $uri @rewrite; # For Drupal <= 6
        try_files $uri /index.php?$query_string; # For Drupal >= 7
    }

    location @rewrite {
        rewrite ^/(.*)$ /index.php?q=$1;
    }

    # Don't allow direct access to PHP files in the vendor directory.
    location ~ /vendor/.*\.php$ {
        deny all;
        return 404;
    }

    # Block access to all /vendor/ files.
    location ^~ /vendor/ {
      deny all;
      return 404;
    }


    # In Drupal 8, we must also match new paths where the '.php' appears in
    # the middle, such as update.php/selection. The rule we use is strict,
    # and only allows this pattern with the update.php front controller.
    # This allows legacy path aliases in the form of
    # blog/index.php/legacy-path to continue to route to Drupal nodes. If
    # you do not have any paths like that, then you might prefer to use a
    # laxer rule, such as:
    #   location ~ \.php(/|$) {
    # The laxer rule will continue to work if Drupal uses this new URL
    # pattern with front controllers other than update.php in a future
    # release.
    location ~ '\.php$|^/update.php' {
        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;
        # Block httpoxy attacks. See https://httpoxy.org/.
        fastcgi_param HTTP_PROXY "";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_intercept_errors on;
        # PHP 5 socket location.
        #fastcgi_pass unix:/var/run/php5-fpm.sock;
        # PHP 7 socket location.
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }

    # Fighting with Styles? This little gem is amazing.
    # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6
    location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7
        try_files $uri @rewrite;
    }

    # Handle private files through Drupal. Private file's path can come
    # with a language prefix.
    location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7
        try_files $uri /index.php?$query_string;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        try_files $uri @rewrite;
        expires max;
        log_not_found off;
    }

}

WordPress Nginx config (default)

server {
    listen 8080;
    listen [::]:8080;
    root /var/www/wp/web;
    index index.php index.html index.htm;
    server_name wp.local;

    location / {
    try_files $uri $uri/ /index.php?$args;      
    }

    location ~ \.php$ {
        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;
        # Block httpoxy attacks. See https://httpoxy.org/.
        fastcgi_param HTTP_PROXY "";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_intercept_errors on;
        # PHP 5 socket location.
        #fastcgi_pass unix:/var/run/php5-fpm.sock;
        # PHP 7 socket location.
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }

}

Nginx error.log has no errors, access.log:

127.0.0.1 - - [26/Apr/2020:21:07:07 +0300] "GET / HTTP/1.1" 200 3582 "http://d8.local:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"
127.0.0.1 - - [26/Apr/2020:21:07:07 +0300] "GET /core/misc/normalize-fixes.css?q95i75 HTTP/1.1" 200 0 "http://d8.local:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"

Update Has nothing to do with Drupal, same issue with WordPress installation or anything that needs PHP.

Update2 Added configs

Update3 So apparently it kind of gets solved if I add fastcgi_read_timeout 1; to location ~ '\.php$|^/update.php' { block (Drupal config), pages now load on 1 second, but getting error for every request:

 [error] 21920#21920: *8 upstream timed out (110: Connection timed out) while reading upstream, client: 127.0.0.1, server: d8.local, request: "GET /contact HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.2-fpm.sock:", host: "d8.local:8080", referrer: "http://d8.local:8080/"

So e.g. if I set it to fastcgi_read_timeout 30; it will load the same page until 30 seconds has passed.

It doesn't load the page completely tho, e.g. it doesn't load the elements in the footer, you have to refres 20 times to load it at least once.

If you set to 0 you get 502 bad gateway with similar error:

[error] 22007#22007: *1 upstream timed out (110: Connection timed out) while reading upstream, client: 127.0.0.1, server: d8.local, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.2-fpm.sock:", host: "d8.local:8080", referrer: "http://d8.local:8080/contact"

Update 2020-05-09 Might be Windows or WSL issue, I deployed same setup with Debian on same machine and get same problem.

I get same problem with simple <?php echo '<p>Hello World</p>'; ?> (I do not get the problem with static HTML without any PHP code in it)

enter image description here

I will try VirtualBox next on same machine, will keep updated.

Update 2020-05-09 2 I do not get this issue with Apache2 + PHP-FPM on same instance of Ubuntu with Nginx disabled.

Best Answer

To fix this on WSL2 (Ubuntu 20.04) with nginx and PHP 7.3 FPM I added

fastcgi_buffering off;

to the PHP location block. Thanks to this SE question/answer: https://stackoverflow.com/questions/52416299/php7-2-fpm-timeout-nginx-wsl-18-04