Claudio4's wiki

Search IconIcon to open search

Configure Nginx for security and performance

Last updated

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
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
user www-data;

# Create as many workers as CPU cores.
worker_processes auto;
# Set the limit of open files for workers processes. The OS defaults to 2000.
worker_rlimit_nofile 100000;

# Only log critical errors. 
error_log /var/log/nginx/error.log crit;

events {
    # Enabling this setting may increase performance in some cases, but it also risks overflowing the workers, degrading performance.
    multi_accept  off;

    # Maximum number of connections per worker.
    worker_connections 4096;

    # Nginx should default to this, but better safe than sorry.
    # Probably a good idea to use io_uring instead if Nginx ever decides to support it.
    use epoll;
}

http {
    # Security by obscurity offers no real defense, but there is no need to tell the world which version of Nginx we are using.
    server_tokens off;

    # More obscurity.
    proxy_hide_header X-Powered-By;
    proxy_hide_header Server;
    proxy_hide_header X-AspNetMvc-Version;
    proxy_hide_header X-AspNet-Version;

    # cache information about FDs, frequently accessed files
    # can boost performance, but you need to test those values
    open_file_cache max=200000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    # Disabling access log boots HDD I/O performance
    access_log off;

    # Use Linux sendfile syscall when possible. Boost performance.
    sendfile on;
    # Limits the amount of data that can be transferred in a single sendfile call. Without the limit, one fast connection may seize the worker process entirely.
    # Nginx 1.21.4+ already set this to 2m, by default older version impose no limit by default.
    sendfile_max_chunk 2m;
    # Tries to send headers and files in the minimum number of packets (aka uses full packets).
    # Reduces network overhead and improves performance.
    tcp_nopush on;


    # Enable gzip compression. The CPU time spent is normally a good tradeoff for the reduced network transfer times.
    gzip on;
    # Compressing files too small produce no benefit.
    gzip_min_length 10240;
    # Turning off this setting makes the HTTP response one header smaller at the expense of breaking clients without gzip support if a pull CDN is in use.
    gzip_vary on;
    # Compress proxied responses.
    gzip_proxied expired no-cache no-store private auth;
    # Only compress text-based files as those are the ones that benefit the most.
    # Nginx always compress HTML and adding it to the list generates a warning.
    gzip_types
        text/css
        text/javascript
        text/xml
        text/plain
        text/x-component
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/rss+xml
        application/atom+xml
        font/truetype
        font/opentype
        application/vnd.ms-fontobject
        image/svg+xml;

    # Eagerly free timed out sockets' buffers.
    reset_timedout_connection on;

    # Close connection if the client takes too long between sent body packages. default is 60.
    client_body_timeout 10s;
    # Close connection if the client does ACK received content in 5 seconds. Default is 60.
    send_timeout 5s;
    # Close keepalive connections after a minute.
    keepalive_timeout 60s;

    # Redirect HTTP traffic to HTTPS by default.
    server {
        listen 80 default_server reuseport;
        return 301 https://$host$request_uri;
    }

    # Answer unrecognized domain with a 403.
    # it's also a great place to put "reuseport" as it can only be stated
    # once for each address:port pair, as once it is activated for said pair
    # it will affect all listen directives that use that pair.
    # 'reuseport' enables socket sharding, increasing performance.
    server {
        listen 443 ssl reuseport http2;
        listen [::]:443 ssl reuseport http2;

        ssl_certificate /path/to/signed_cert_plus_intermediates;
        ssl_certificate_key /path/to/private_key;

        return 403;
    }

    # Set apppiate mime-type based on the extension
    include /etc/nginx/mime.types;
    # octet-stream is  the mime type any generic binary data.
    default_type application/octet-stream;

    # Reuse the tls session for a day. Reduce TLS handshake of future connections, increasing performance.
    ssl_session_timeout 1d;
    # 50MiB cache. About 200000 sessions.
    ssl_session_cache shared:SSL:50m;
    # SSL session cache is better than tickets in any way possible.
    ssl_session_tickets off;
    
    # Anything older than TLSv1.2 is insecure.
    ssl_protocols TLSv1.2 TLSv1.3;
    
    # generate with openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    
    # Use secure TLSv1.2 cipher (TLS1.3 ignores this directive).
    # These cipher does not support RSA certificates, this is by design.
    # If you want security and performance and use RSA certificates you are doing something wrong.
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;
    # The client tends to have a weaker CPU thant the server (yes I know that a server handles multiples clients), so choosing a cipher that favors it can speed up things, especially with low-end mobile phones.
    ssl_prefer_server_ciphers off;

    # Load extra sites
    include /etc/nginx/sites/*.conf;
}

secure-headers.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

# Add HSTS for 2 years.
add_header Strict-Transport-Security "max-age=63072000" always;
# Only allow the site to be in iframes in the same domain. Prevents Click-jacking attacks.
add_header X-Frame-Options "SAMEORIGIN";
# Force browsers to respect server mimetype.
add_header X-Content-Type-Options "nosniff";
# Prevents leaks of GET params to other sites.
add_header Referrer-Policy "origin-when-cross-origin";

# Be cautious as this may break some sites.
# CSP is a great security tool, but it should fine tubed in a per site basis.
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none'";

Example site

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate /path/to/signed_cert_plus_intermediates;
    ssl_certificate_key /path/to/private_key;

    # Add secure headers
    include /etc/nginx/secure-headers.conf;

    server_name example.com;
    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

OCSP Stampling

Just don’t, it’s not worth it. OCSP without OCSP must-staple is worthless, but if you want to use must-staple with Nginx’s horrendous OCSP sampling ways, well… I wish you good luck. And even if you get it to work, it’s still worthless as Chromium browsers (aka most of the clients) do not honor the must-staple extension.

If you want to protect your site against the possibility of stolen certificates, it’s better to just use short-lived certificates.

Thanks to


Interactive Graph