FastCGI to improve TTFB when Using NGINX to reverse proxy?

July 3, 2019 483 views
Nginx Apache Node.js Server Optimization Ubuntu 18.04

I’m trying to improve time to first byte. HTTP2 helped a lot, but TTFB is still between 500-700ms. I’d like to bring it down to around 200.

I saw some suggestions to use FastCGI. I have a bit of a tricky setup so I’m trying to understand where/how I would set it up:

I have a wordpress app on Apache server, listening on port 8090. This is simply to have a wordpress dashboard, expose API endpoints and to serve media. My second NextJS app is using nodejs listening on port 8000. This consumes the wordpress API and actually renders the site.

Nginx is in front of both of these apps to do https/www redirects, and proxy requests to the respective ports.

As far as I can see at least all assets and pages are served by nginx. e.g.: curl -I -L https://www.example.com/wordpress/wp-content/uploads/2015/05/image.jpg - response headers tell me it’s nginx serving them.

But I’m still confused as to whether setting up FastCGI on Nginx makes sense. I’m still guessing that it’s actually Apache handling the PHP execution because I originally set up working wordpress with the default apache droplet, and put Nginx in front of that later. Am I wrong? What’s my path to improving the TTFB in this case? Perhaps I should even be looking at my Node app, but I’m just not sure how to identify where the delay is coming from.

FYI Here’s my /etc/nginx/sites-available/example.com:

server {
    listen 80;
    server_name example.com www.example.com;
    rewrite ^/(.*) https://www.example.com/$1 permanent;
}
server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;
        root /var/www/html;

        index index.php index.js index.html index.htm index.nginx-debian.html;

        server_name example.com;
        return 301 https://www.example.com$request_uri;
        ssl_certificate /home/ssl/example.com.chained.crt;
        ssl_certificate_key /home/ssl/example.com.key;

        location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        }

        location /wordpress {
        proxy_pass http://localhost:8090;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        }
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        root /var/www/html;

        index index.php index.js index.html index.htm index.nginx-debian.html;

        server_name www.example.com;
        ssl_certificate /home/ssl/example.com.chained.crt;
        ssl_certificate_key /home/ssl/example.com.key;

        location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        }

        location /wordpress {
        proxy_pass http://localhost:8090;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        }
}
2 Answers

Hello,

There would be no need to use FCGI with Nginx in your case as Apache is handling that for you.

What you could look into is enabling proxy cache in your Nginx conf for some static files like *.jpg, *.html, *.gif, etc. That way those files would be cached and this means that nginx won’t even need to talk to the back-end for those requests.

Hope that this helps.
Bobby

@geochanto

If Apache is currently handling PHP requests, you wouldn’t need to use PHP-FPM with NGINX unless you’re looking to replace Apache and use NGINX exclusively. Unlike Apache, NGINX doesn’t provide modules for PHP. While it does handle PHP requests using FCGI, it relies on PHP-FPM being installed.

NGINX is fully capable of handling both proxying and PHP-FPM in the same server block, and will often provide noticeable speed improvements in comparison to a dual web server configuration.

In using NGINX exclusively, you’re eliminating the need for NGINX to pass the request over to the backend (Apache) and instead, NGINX will either handle the request directly (in the case of assets) or pass the request to either PHP-FPM for processing or your NodeJS app, allowing NodeJS to handle the request.

-

One quick recommendation would be to consolidate your configuration and update the redirect for port 80 => 443. The server_name directive can handle both hostnames which would prevent the need for managing multiple server blocks for the same host.

For example, and using your provided configuration, we could consolidate that down to:

server {
    listen 80;
    server_name example.com www.example.com;

    #
    # Replacing the rewrite with a 301 redirect
    # $host will handle requests for either example.com or www.example.com
    # $request_uri will handle the query string that was previously handle by the rewrite (i.e. $1)
    #
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    root /var/www/html;

    index index.php index.js index.html index.htm index.nginx-debian.html;

    #
    # Adding www.example.com to server_name
    #
    server_name example.com www.example.com;

    return 301 https://www.example.com$request_uri;

    ssl_certificate /home/ssl/example.com.chained.crt;
    ssl_certificate_key /home/ssl/example.com.key;

    location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    location /wordpress {
        proxy_pass http://localhost:8090;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

The only issue I can spot in the configuration would be with the return statement that rewrites the request a second time.

return 301 https://www.example.com$request_uri;

If the request is coming in from HTTP, being rewritten and passed to HTTPS, and then rewritten again, that will likely slow the request down. Unless this is specific to your application and required, it could be removed leaving you with:

server {
    listen 80;
    server_name example.com www.example.com;

    #
    # Replacing the rewrite with a 301 redirect
    # $host will handle requests for either example.com or www.example.com
    # $request_uri will handle the query string that was previously handle by the rewrite (i.e. $1)
    #
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    root /var/www/html;

    index index.php index.js index.html index.htm index.nginx-debian.html;

    #
    # Adding www.example.com to server_name
    #
    server_name example.com www.example.com;

    ssl_certificate /home/ssl/example.com.chained.crt;
    ssl_certificate_key /home/ssl/example.com.key;

    location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    location /wordpress {
        proxy_pass http://localhost:8090;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
  • Hey thanks for the tip… but trying your config without the return statement in the second server block, non-www no longer redirects to www, which is what I needed to keep.

    With the return statement, I’m getting “www.example.com redirected you too many times”.

    Reason I had it set up the way I had it was to redirect http traffic to https, and to redirect non-www to www.

    Regarding your suggestion to switch over to Nginx, well static files are already served by Nginx. Are you saying that Nginx with php-fpm will be faster than Apache with mod_php? I’ve read some mixed opinions about that. Do you mean it would be faster to execute, or faster to respond?

Have another answer? Share your knowledge.