Sponsored By: Password Angel - Share passwords, API keys, credentials and more with secure, single-use links.

Nginx configuration for a Symfony project

Full Example

server {
    listen   80;

    server_name supercoolwidgets.xyz my.supercoolwidgets.xyz;

    rewrite ^(.*)$ https://$host$1 permanent;
}

server {
    listen 443 ssl http2;

    access_log  /var/log/nginx/access.log  main  buffer=256k flush=10s;

    ssl_certificate /etc/letsencrypt/live/supercoolwidgets.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/supercoolwidgets.xyz/privkey.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload;";

    root /data/sites/supercoolwidgets.xyz/public;
    index index.html index.php;

    server_name supercoolwidgets.xyz my.supercoolwidgets.xyz;

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;

    location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass 127.0.0.1:9000;

        # If you're using unix socket change fastcgi_pass to:
        #fastcgi_pass unix:/var/run/php-fpm.sock;

        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        fastcgi_param APP_ENV prod;

        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # Return 404 for all other php files not matching the front controller
    # This prevents access to other php files you don't want to be accessible.
    location ~ \.php$ {
        return 404;
    }
}

Explanation

HTTP -> HTTPS redirect

server {
    listen   80;

    server_name supercoolwidgets.xyz;

    rewrite ^(.*)$ https://$host$1 permanent;
}

This Nginx server block listens on port 80 (HTTP) for requests directed to the domain "supercoolwidgets.xyz". It uses a rewrite rule to redirect all incoming HTTP requests to their corresponding HTTPS URLs, effectively enforcing a permanent (301) redirect to the secure version of the website. This ensures that users always access the website over a secure HTTPS connection, even if they initially enter the non-secure "http://" version of the URL.

Logging, SSL Certificates & HTTPS

    listen 443 ssl http2;

    access_log  /var/log/nginx/access.log  main  buffer=256k flush=10s;

    ssl_certificate /etc/letsencrypt/live/supercoolwidgets.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/supercoolwidgets.xyz/privkey.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; 

This enables HTTPS on port 443, http2, access logging, SSL/TLS encryption, and sets the Strict Transport Security (HSTS) header. The access_log directive specifies that Nginx should log requests to "/var/log/nginx/access.log" using the "main" log format with a buffer size of 256KB and a flush interval of 10 seconds, which optimizes log writing performance.

The ssl_certificate and ssl_certificate_key directives point to the SSL certificate and private key files needed for secure connections. They are located at "/etc/letsencrypt/live/supercoolwidgets.xyz/fullchain.pem" and "/etc/letsencrypt/live/supercoolwidgets.xyz/privkey.pem", respectively.

The add_header directive sets the HSTS header with a max-age of 1 year (31536000 seconds), instructing the browser to only access the website over HTTPS for that duration. It also includes all subdomains in the HSTS policy for added security.

Gzip Compression

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;

This enables Gzip compression for specific types of files, aiming to improve website performance by reducing the size of data sent to clients. The gzip on; directive turns on Gzip compression, while gzip_disable "msie6"; disables compression for clients using the obsolete MSIE6 browser.

gzip_vary on; includes the "Vary: Accept-Encoding" header in responses, informing caches that the response content may vary based on the client's preferred compression method. gzip_proxied any; enables compression for both proxied and non-proxied requests.

gzip_comp_level 6; sets the compression level to 6, balancing compression efficiency and CPU usage. gzip_buffers 16 8k; sets the compression buffer size to 16 and 8KB respectively.

gzip_http_version 1.1; specifies that only HTTP version 1.1 and above clients should receive compressed responses. gzip_min_length 256; indicates that files smaller than 256 bytes won't be compressed.

Finally, the gzip_types directive specifies the file types that should be compressed, including plain text, CSS, JavaScript, XML, JSON, font files, and various image formats.

Default Location Block

   location / {
        # try to serve file directly, fallback to index.php
        try_files $uri /index.php$is_args$args;
    }

This defines a location block for requests to the website's root URL ("/"). The try_files directive is used to handle these requests. It attempts to serve the requested file directly using the $uri variable, and if the file is not found, it falls back to serving the "index.php" file with the original query parameters (if any) using the $is_args$args variable.

In summary, when a client makes a request to the root URL of the website, Nginx will try to find and serve the corresponding file directly. If the file is not found, it will route the request to the "index.php" file, typically used as a front controller for the web application, passing along any query parameters from the original request. This is a common approach in PHP-based applications to handle clean URLs and dynamic content generation.

Front Controller Location Block

location ~ ^/index\.php(/|$) {
        fastcgi_pass 127.0.0.1:9000;

        # If you're using unix socket change fastcgi_pass to:
        #fastcgi_pass unix:/var/run/php-fpm.sock;

        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # optionally set the value of the environment variables used in the application
        fastcgi_param APP_ENV prod;

        # fastcgi_param APP_SECRET <app-secret-id>;
        # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        # When you are using symlinks to link the document root to the
        # current version of your application, you should pass the real
        # application path instead of the path to the symlink to PHP
        # FPM.
        # Otherwise, PHP's OPcache may not properly detect changes to
        # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
        # for more information).
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/index.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

This location block handles requests for PHP files with paths starting with "/index.php" or URLs containing only "/index.php". It is commonly used as a front controller for PHP-based web applications. Here's a summary of its functionality:

When Nginx receives a request matching the pattern "/index.php" or URLs like "/index.php/some-path", it processes the request using the FastCGI protocol and forwards it to the PHP-FPM server running on the local IP address 127.0.0.1 and port 9000. Alternatively, if you are using a Unix socket for PHP-FPM, you can uncomment and use the "fastcgi_pass unix:/var/run/php-fpm.sock;" line.

The "fastcgi_split_path_info" directive captures the PHP file path and any additional path information to be used in the request.

The "include fastcgi_params;" line includes common FastCGI parameters required for proper communication between Nginx and PHP-FPM.

Several "fastcgi_param" directives set environment variables used by the PHP application. In this example, it sets the "APP_ENV" variable to "prod" (production environment), but you can customize these variables to suit your application's needs.

The "SCRIPT_FILENAME" and "DOCUMENT_ROOT" directives specify the real paths for the PHP script to be executed and the document root for the application, respectively. This is essential when using symlinks to ensure proper caching behaviour with PHP's OPcache.

The "internal;" directive prevents direct access to URLs containing the front controller ("/index.php") and results in a 404 Not Found error for such requests. It is used to avoid exposing the front controller's internal workings to users directly.

In conclusion, this location block acts as the entry point for PHP-based web applications, forwarding PHP requests to the PHP-FPM server and setting necessary environment variables for the application to run correctly. It also includes security measures to prevent direct access to the front controller URL.

Block access to other PHP files

    location ~ \.php$ {
        return 404;
    }

This location block handles requests for any file with the ".php" extension. It is used to prevent direct access to PHP files from the web server and return a 404 Not Found error for any such requests.

When Nginx receives a request for a URL that ends with ".php", it will match this location block. Instead of executing the PHP script or passing the request to a PHP handler, the "return 404;" directive is used to send a 404 Not Found HTTP response back to the client. This effectively blocks direct access to PHP files and prevents potential security risks by ensuring that PHP files are not accessible and can only be executed through the designated entry points defined in the configuration (such as the front controller).

Additional Reading