Drupal 8/9, nginx and Let's Encrypt

This site is powered by Drupal 10. I'm using Nginx as a web server and Let's Encrypt to add HTTPS encryption to my site. In this blog post, I will show you how my Nginx config file looks like.

My domain is gorannikolovski.com

All requests are redirected to the https-without-www version. That means that if a user types any of these:

  • http://www.gorannikolovski.com
  • https://www.gorannikolovski.com
  • http://www.gorannikolovski.com

he or she will be redirected to https://gorannikolovski.com

Here is my Nginx config file:

server {
  server_name gorannikolovski.com;
  root /var/www/gorannikolovski.com/web; ## <-- Your only path reference.

  access_log /var/log/web/gorannikolovski.com_access_log;
  error_log /var/log/web/gorannikolovski.com_error_log;

  gzip on;
  gzip_comp_level 6;
  gzip_vary on;
  gzip_min_length 1000;
  gzip_proxied expired no-cache no-store private auth;
  gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
  gzip_disable "MSIE [1-6]\.";

  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)$ {
    allow 192.168.0.0/16;
    deny all;
  }

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

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

  # 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;
  }

  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;
  }

  # 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.
    fastcgi_param APP_ENV GOCLOUD-LIVE;
    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;
    fastcgi_pass 127.0.0.1:9000;
  }

  # 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;
  }

  listen 443 ssl; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/gorannikolovski.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/gorannikolovski.com/privkey.pem; # managed by Certbot
}

server {
  if ($host = gorannikolovski.com) {
    return 301 https://$host$request_uri;
  } # managed by Certbot

  server_name gorannikolovski.com;
  listen 80;
  return 404; # managed by Certbot
}

server {
  listen 80;
  server_name www.gorannikolovski.com;
  return 301 https://gorannikolovski.com$request_uri;
}

server {
  listen 443 ssl http2;
  server_name www.gorannikolovski.com;
  ssl_certificate /etc/letsencrypt/live/gorannikolovski.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/gorannikolovski.com/privkey.pem;
  return 301 https://gorannikolovski.com$request_uri;
}

Code blocks that are commented with # managed by Certbot are automatically inserted by Certbot. Certbot is a tool that is installed on your server and by running one command:

certbot

you can choose domains for which you want to add Let's Encrypt HTTPS encryption. This is how that looks like:

Image

Now, that's pretty cool.

Certbot will update your config files so that you don't have to manually do that.  I'm using Ubuntu 20.04 on my server so I followed these instructions: ubuntubionic-nginx to install Certbot, but there are also instructions for many other systems.

About the Author

Goran Nikolovski is an experienced web and AI developer skilled in Drupal, React, and React Native. He founded this website and enjoys sharing his knowledge.