Getting Started with NGINX (Part 2): Advanced Configuration

Traducciones al Español
Estamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Create a Linode account to try this guide with a $ credit.
This credit will be applied to any valid services used during your first  days.

Before You Begin

  • This guide is Part 2 of our Getting Started with NGINX series, and you will need a working NGINX setup with a website accessible via HTTP. If you do not already have that, complete Part 1: Basic Installation and Setup.

  • You will need root access to the system, or a user account with sudo privilege.

  • You may want to make another backup of your nginx.conf so you have a snapshot of the work you’ve done up to this point:

    cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup-pt2
    

Configuration Notes

The internet has no shortage of sites, posts, and other places listing “tuning”, “tweaking”, or “optimizing” procedures for NGINX.

However, rarely are these configurations tested to deduce if there is in fact a performance increase. Of those that are, the author’s use case may be completely different than yours, so there’s no guarantee you’ll experience the same benefit using their configuration.

Favor simplicity and your own results; do not blindly follow tuning guides you find on the internet which haphazardly present their configuration as one-size-fits-all advice. There are some config options which are virtually universal, and we use many of them in this series. Beyond that, you could actually be decreasing your server’s performance and/or security.

Truly advanced system tuning for web services, such as adjusting Linux kernel parameters and TCP stack functionality, is out of the scope of this series. If you would like to explore the topic further, a good place to start is this NGINX blog post.

Host Multiple Websites

In NGINX speak, a Server Block basically equates to a website (same as Virtual Host in Apache terminology). NGINX can host multiple websites, and each site’s configuration should be in its own file, with the name formatted as example.com.conf. That file should be located at /etc/nginx/conf.d/.

If you then want to disable the site example.com, then rename example.com.conf to example.com.conf.disabled. When hosting multiple sites, be sure to separate their access and error logs with specific directives inside each site’s server block. See Server Block Examples in the NGINX docs for more information.

  1. Provided that you already have one site configuration running on NGINX, all the second site’s configuration file needs is a server block inside:

    File: /etc/nginx/conf.d/example2.com.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    server {
        listen       80;
        listen       [::]:80;
        server_name  example2.org www.example2.org;
        access_log   logs/example2.access.log main;
        error_log    logs/example2.error error;
    
        root         /var/www/example2.com/;
    }
  2. Reload NGINX:

    nginx -s reload
    

    Your second website should be visible at its domain and/or IP address.

Basic NGINX Caching

NGINX can cache files served by web applications and frameworks such as WordPress, Drupal and Ruby on Rails. Though covering caching at this point steps out of the basic workflow so far (we haven’t set up any application with data to cache yet), it’s worth mentioning here briefly.

For more information, see the NGINX docs, NGINX admin guide, and the NGINX blog.

  1. Create a folder to store cached content:

    mkdir /var/www/example.com/cache/
    
  2. Add the proxy_cache_path directive to NGINX’s http block. Make sure the file path references the folder you just created in Step 1.

    File: /etc/nginx/nginx.conf
    1
    
    proxy_cache_path /var/www/example.com/cache/ keys_zone=one:10m max_size=500m inactive=24h use_temp_path=off;
    • keys_zone=one:10m sets a 10 megabyte shared storage zone (simply called one, but you can change this for your needs) for cache keys and metadata.

    • max_size=500m sets the actual cache size at 500 MB.

    • inactive=24h removes anything from the cache which has not been accessed in the last 24 hours.

    • use_temp_path=off writes cached files directly to the cache path. This setting is recommended by NGNIX.

  3. Add the following to your site configuration’s server block. If you changed the name of the storage zone in the previous step, change the directive below from one to the zone name you chose.

    Replace ip-address and port with the URL and port of the upstream service whose files you wish to cache. For example, you would fill in 127.0.0.1:9000 if using WordPress or 127.0.0.1:2638 with Ghost.

    File: /etc/nginx/conf.d/example.com.conf
    1
    2
    3
    4
    
    proxy_cache one;
        location / {
        proxy_pass http://ip-address:port;
        }
  4. If you need to clear the cache, the easiest way is with the command:

    find /var/www/example.com/cache/ -type f -delete
    

    If you want more than just a basic cache clear, you can use the proxy_cache_purge directive.

HTTP Response Header Fields

Use add_header directives in your configuration carefully. Unlike other directives, an add_header directive is not inherited from parent configuration blocks. If you have the directive in both, an add_header directive in a server block will override any in your http area.

For this reason, you should include them in one of two different ways:

  • Put all add_header directives in the http block. This isn’t practical unless you want every header directive to apply to every site in your configuration.

  • Add the desired add_header directives only to the server block (or an include file) of the site you want those directives applied to. This is the best scenario if you have multiple websites and want some header directives applied to some sites, but not all sites you’re hosting.

Below are some of the more universally-applicable header modifications. There are many more available, and you should read through the OWASP Secure Headers Project for more information.

Disable Content Sniffing

Content sniffing allows browsers to inspect a byte stream in order to determine the file format of its contents. It is generally used to help sites that do not correctly identify the MIME type of their content, but it also presents a vector for cross-site scripting and other attacks. To disable content sniffing, add the following line to your configuration’s http block:

add_header X-Content-Type-Options nosniff;

Limit or Disable Content Embedding

Content embedding is when a website renders a 3rd party element (div, img, etc.), or even an entire page from a completely different website, in a <frame>, <iframe>, or <object> HTML block on its own site.

The X-Frame-Options HTTP header stops content embedding so your site can’t be presented from an embedded frame hosted on someone else’s website, one undesirable outcome being a clickjacking attack. See X-Frame-Options, Mozilla Developer Network for more information.

To disallow the embedding of your content from any domain other than your own, add the following line to your configuration:

add_header X-Frame-Options SAMEORIGIN;

To disallow embedding entirely, even from within your own site’s domain:

add_header X-Frame-Options DENY;

Cross-Site Scripting (XSS) Filter

This header signals to a connecting browser to enable its cross-site scripting filter for the request responses. XSS filtering is usually enabled by default in modern browsers, but there are occasions where it’s disabled by the user. Forcing XSS filtering for your website is a security precaution, especially when your site offers dynamic content like login sessions:

add_header X-XSS-Protection "1; mode=block";

See the X-XSS-Protection page on MDN for more information and other options.

Referrer Policy

This header controls policy of referrer information that should be included with requests. The example below sends the origin, path, and query string when performing a same-origin request. For secure (HTTPS -> HTTPS) cross-origin requests, only the origin is sent.

add_header Referrer-Policy strict-origin-when-cross-origin;

See the Referrer-Policy page on MDN for more information and other options.

Content Security Policy

Another way to minimize cross-site scripting attacks is by only allowing user agents to load resources specified in the directives of this header. For example, to only allow loading resources from the same origin (URL and port number), and to upgrade insecure requests (HTTP) to HTTPS:

add_header Content-Security-Policy "default-src 'self'; upgrade-insecure-requests;";

See the Content-Security-Policy on MDN for complete list of directives.

Feature Policy (Experimental)

You can allow or deny browser features with this header, depending on whether your site and browser supports the feature. This header should be set on a per site basis. For example, to allow the site to use encrypted media on the same origin and deny automatic playing media without user actions, you could add the following line:

add_header Feature-Policy "encrypted-media 'self'; autoplay 'none'";

Configuration Recap

To summarize where we are so far:

  • We’re continuing with the configuration from Part 1, so we have a single site being served over HTTP.

  • We’ve added the caching and HTTP header changes mentioned above.

  • The site’s configuration file looks like this:

    File: /etc/nginx/conf.d/example.com.conf
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    server {
        listen         80 default_server;
        listen         [::]:80 default_server;
        server_name    example.com www.example.com;
        root           /var/www/example.com;
        index          index.html;
    
        proxy_cache one;
            location / {
                proxy_pass http://localhost:8000;
            }
    
        gzip             on;
        gzip_comp_level  3;
        gzip_types       text/html text/plain text/css image/*;
    
        add_header     Feature-Policy "encrypted-media 'self'; autoplay 'none'"
    }
  • Here is the server’s nginx.conf file. Again, our additions are at the bottom of the block so we know what we added:

    File: /etc/nginx/nginx.conf
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    user  nginx;
    worker_processes  auto;
    
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       /etc/nginx/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  65;
    
        #gzip  on;
    
        include /etc/nginx/conf.d/*.conf;
    
    
        add_header    X-Content-Type-Options nosniff;
        add_header    X-Frame-Options SAMEORIGIN;
        add_header    X-XSS-Protection "1; mode=block";
        add_header    Referrer-Policy strict-origin-when-cross-origin;
        add_header    Content-Security-Policy "default-src 'self'; upgrade-insecure-requests;";
    
        proxy_cache_path    /var/www/example.com/cache/ keys_zone=one:10m inactive=60m use_temp_path=off;
        server_tokens       off;
    }

Part 3: Enable TLS for HTTPS Connections

If a well-running HTTP site is all you’re looking for, the configurations in this guide will meet that requirement. If you plan to serve your site over HTTPS, then continue to Part 3 of this series: Enable TLS for HTTPS Connections.

This page was originally published on


Your Feedback Is Important

Let us know if this guide was helpful to you.


Join the conversation.
Read other comments or post your own below. Comments must be respectful, constructive, and relevant to the topic of the guide. Do not post external links or advertisements. Before posting, consider if your comment would be better addressed by contacting our Support team or asking on our Community Site.
The Disqus commenting system for Linode Docs requires the acceptance of Functional Cookies, which allow us to analyze site usage so we can measure and improve performance. To view and create comments for this article, please update your Cookie Preferences on this website and refresh this web page. Please note: You must have JavaScript enabled in your browser.