Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Essential Nginx patterns

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Essential Nginx patterns

Avatar for Matteo Bertozzi

Matteo Bertozzi

December 23, 2024
Tweet

More Decks by Matteo Bertozzi

Other Decks in Programming

Transcript

  1. Reverse Proxy server { listen 80; server_name services.example.com; location /service_a/

    { proxy_pass http://192.168.18.21:3000/; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx services.example.com $ curl http://services.example.com/service_a/foo Using SubDirectories
  2. Reverse Proxy server { listen 80; server_name services.example.com; location /service_a/

    { proxy_pass http://192.168.18.21:3000/; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx services.example.com The trailing slash is important to avoid forwarding the /service_a/ part to the target service Using SubDirectories
  3. Reverse Proxy server { listen 80; server_name services.example.com; location /service_a/

    { proxy_set_header 'X-Foo' 'test-injected-value'; proxy_pass http://192.168.18.21:3000/; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx services.example.com You can add/modify/delete headers before forwarding the request to the service Using SubDirectories
  4. Reverse Proxy server { listen 80; server_name services.example.com; location /service_a/

    { proxy_set_header 'X-Foo' 'test-injected-value'; proxy_pass http://192.168.18.21:3000/; add_header 'X-Resp-Bar' 'test-injected-rvalue'; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx services.example.com You can add/modify/delete headers before sending the response back to the client Using SubDirectories
  5. Reverse Proxy server { listen 80; server_name services.example.com; location /service_a/

    { client_max_body_size 2M; proxy_set_header 'X-Foo' 'test-injected-value'; proxy_pass http://192.168.18.21:3000/; add_header 'X-Resp-Bar' 'test-injected-rvalue'; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx services.example.com You can force a limit on the request body size. A 413 Request Entity Too Large will be returned by nginx before forwarding the request to the backend. Using SubDirectories
  6. Reverse Proxy server { listen 80; server_name services.example.com; location /service_a/

    { proxy_pass http://192.168.18.21:3000/; } location /service_b/ { proxy_pass http://192.168.18.21:3001/; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx services.example.com $ curl http://services.example.com/service_a/foo $ curl http://services.example.com/service_b/bar Using SubDirectories
  7. Reverse Proxy server { listen 80; server_name service-a.example.com; location /

    { proxy_pass http://192.168.18.21:3000/; } } server { listen 80; server_name service-b.example.com; location / { proxy_pass http://192.168.18.21:3001/; } } Private Network Service A Service B 192.168.18.21:3000 192.168.18.21:3001 Public nginx service-a.example.com
 service-b.example.com $ curl http://service-a.example.com/foo $ curl http://service-b.example.com/bar Using SubDomains
  8. upstream my_backend_foo { server 192.168.18.21:3000; } upstream my_backend_bar { server

    192.168.18.21:3001; } server { listen 80; server_name ~^(?<serviceName>.+)\.example\.com; location / { proxy_pass http://my_backend_$serviceName; } } Reverse Proxy Private Network Service Foo Service Bar 192.168.18.21:3000 192.168.18.21:3001 Public nginx foo.example.com
 bar.example.com Using SubDomains $ curl http://foo.example.com/x2 $ curl http://bar.example.com/x2
  9. Load Balancing upstream my_backend_machines { server 192.168.18.21:3000 weight=2; server 192.168.18.21:3001;

    server 192.168.18.22:3000 backup; } server { listen 80; server_name _; location / { proxy_pass http://my_backend_machines; } } Round Robin Service 192.168.18.21:3000 Service 192.168.18.21:3001 Service 192.168.18.22:3000
  10. Load Balancing IP Hash upstream my_backend_machines { ip_hash; server 192.168.18.21:3000;

    server 192.168.18.21:3001; server 192.168.18.22:3000; } server { listen 80; server_name _; location / { proxy_pass http://my_backend_machines; } } Service 192.168.18.21:3000 Service 192.168.18.21:3001 Service 192.168.18.22:3000 Machine Selection Policy
  11. Load Balancing Field Hash Service 192.168.18.21:3000 Service 192.168.18.21:3001 Service 192.168.18.22:3000

    You can hash on any field/variable upstream my_backend_machines { hash $http_x_my_field consistent; server 192.168.18.21:3000; server 192.168.18.21:3001; server 192.168.18.22:3000; } server { listen 80; server_name _; location / { proxy_pass http://my_backend_machines; } } In this case, header: X-My-Field: foo
  12. Split Traffic upstream my_service_standard { server 192.168.18.21:3000; } upstream my_service_test_p10

    { server 192.168.18.21:3001; } upstream my_service_test_p20 { server 192.168.18.22:3000; } split_clients "${remote_addr}" $variant { 10.0% test_p10; 20.0% test_p20; * standard; } server { listen 80; server_name _; location / { proxy_pass http://my_service_$variant; } } 10% 20% 70% You can hash on any field/variable
  13. server { listen 80; server_name _; location / { mirror

    /mirror; mirror_request_body on; proxy_pass http://192.168.18.21:3001; } location = /mirror { internal; proxy_pass http://192.168.18.21:3002$request_uri; } } Mirror Traffic Responses to mirror subrequests are ignored Service 192.168.18.21:3001 Mirror Service 192.168.18.21:3002
  14. Feature Flags server { listen 80; server_name _; location /

    { if ($http_x_features ~* "(^|,)foo(,|$)") { proxy_pass http://192.168.18.21:3001; break; } if ($http_x_features ~ "(^|,)bar(,|$)") { proxy_pass http://192.168.18.21:3002; break; } proxy_pass http://192.168.18.21:3000; } } standard with "foo" with "bar"
  15. Feature Flags upstream my_service_with_bar { server 192.168.18.21:3002; } upstream my_service_with_foo

    { server 192.168.18.21:3001; } upstream my_service_standard { server 192.168.18.21:3000; } map $http_x_features $feature_match { default standard; ~(^|,)foo(,|$) with_foo; ~(^|,)bar(,|$) with_bar; } server { listen 80; server_name _; location / { proxy_pass http://my_service_$feature_match; } } standard with "foo" with "bar"
  16. upstream my_service_v10 { server 192.168.18.21:3002; } upstream my_service_v15 { server

    192.168.18.21:3001; } upstream my_service_v20 { server 192.168.18.21:3000; } server { listen 80; server_name _; location / { set $backend ""; access_by_lua_block { local headers = ngx.req.get_headers() local min_version = tonumber(headers["X-Min-Required-Version"]) or 0 if min_version > 15 then ngx.var.backend = "service_v20" elseif min_version > 10 then ngx.var.backend = "service_v15" else ngx.var.backend = "service_v10" end } proxy_pass http://$backend; } } Min Required Version LUA Extension v10 v15 v20
  17. upstream my_service_v10 { server 192.168.18.21:3002; } upstream my_service_v15 { server

    192.168.18.21:3001; } upstream my_service_v20 { server 192.168.18.21:3000; } js_import my_module.js; server { listen 80; server_name _; location / { js_set $backend my_module.determine_backend; proxy_pass http://$backend; } } Min Required Version NJS Extension v10 v15 v20 // my_module.js function determine_backend(r) { const minVersion = parseInt(r.headersIn[‘X-Min-Required-Version']) || 0; if (minVersion > 15) { return 'my_service_v20'; } else if (minVersion > 10) { return 'my_service_v15'; } else { return 'my_service_v10'; } } export default { determine_backend };
  18. >= 1.27.3 Dynamic Upstream with SRV DNS Record upstream my_backend_machines

    { zone my_backends 64k; resolver 8.8.8.8; server backends.example.com service=http resolve; } $ nslookup -q=SRV _http._tcp.backends.example.com _http._tcp.backends.example.com. priority=10 weight=5 port=3000 backend-0.example.com. _http._tcp.backends.example.com. priority=10 weight=5 port=3001 backend-0.example.com. _http._tcp.backends.example.com. priority=10 weight=5 port=3000 backend-1.example.com. _http._tcp.backends.example.com. priority=10 weight=5 port=3000 backend-2.example.com. upstream my_backend_machines { server 192.168.18.11:3000; # backend-0 server 192.168.18.11:3001; # backend-0 server 192.168.18.12:3000; # backend-1 server 192.168.18.13:3000; # backend-2 }
  19. Handling Redirects location /foo/ { proxy_pass http://192.168.20.21:3000/; proxy_intercept_errors on; error_page

    301 302 307 = @handle_redirects; } location @handle_redirects { set $original_uri $uri; set $orig_loc $upstream_http_location; proxy_pass $orig_loc; } Inside nginx
  20. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } } none The Location will be matched against the beginning of the requested URI. $ curl http://localhost/hello
 $ curl http://localhost/helloween $ curl http://localhost/hello/foo
  21. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } location /hello/ { return 200 'hello slash something'; } } none The Location will be matched against the beginning of the requested URI. $ curl http://localhost/hello/ $ curl http://localhost/hello/foo
  22. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } location /hello/ { return 200 'hello slash something'; } location = /hello { return 200 'hello exact'; } } none The Location will be matched against the beginning of the requested URI. = Match a Location exactly against the requested URI. $ curl http://localhost/hello
  23. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } location /hello/ { return 200 'hello slash something'; } location = /hello { return 200 'hello exact'; } location ~ /hello/([_a-z0-9-]+)/ { return 200 'hello/$1 regex slash something'; } } none The Location will be matched against the beginning of the requested URI. = Match a Location exactly against the requested URI. ~ Case-Sensitive regular expression match against the requested URI. $ curl http://localhost/hello/data/ $ curl http://localhost/hello/data/foo

  24. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } location /hello/ { return 200 'hello slash something'; } location = /hello { return 200 'hello exact'; } location ~ /hello/([_a-z0-9-]+)/ { return 200 'hello/$1 regex slash something'; } location ^~ /hello/world/foo { return 200 'hello/world/foo and something'; } } none The Location will be matched against the beginning of the requested URI. = Match a Location exactly against the requested URI. ~ Case-Sensitive regular expression match against the requested URI. ^~ Longest non-regular expression match against the requested URI. 
 If the requested URI matches, no further matching will takes place. $ curl http://localhost/hello/world/foo $ curl http://localhost/hello/world/foo2
 $ curl http://localhost/hello/world/foo/bar
  25. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } location /hello/ { return 200 'hello slash something'; } location = /hello { return 200 'hello exact'; } location ~ /hello/([_a-z0-9-]+)/ { return 200 'hello/$1 regex slash something'; } location ^~ /hello/world/foo { return 200 'hello/world/foo and something'; } location ~* /([_a-z0-9-]+)/xyz { return 200 '$1/xyz regex and something'; } } none The Location will be matched against the beginning of the requested URI. = Match a Location exactly against the requested URI. ~ Case-Sensitive regular expression match against the requested URI. ^~ Longest non-regular expression match against the requested URI. 
 If the requested URI matches, no further matching will takes place. ~* Case-Insensitive regular expression match against the requested URI. $ curl http://localhost/data/xyz $ curl http://localhost/data/xyzkw
 $ curl http://localhost/data/xyz/foo
  26. Location Directive server { listen 80; server_name _; location /hello

    { return 200 'hello and something'; } location /hello/ { return 200 'hello slash something'; } location = /hello { return 200 'hello exact'; } location ~ /hello/([_a-z0-9-]+)/ { return 200 'hello/$1 regex slash something'; } location ^~ /hello/world/foo { return 200 'hello/world/foo and something'; } location ~* /([_a-z0-9-]+)/xyz { return 200 '$1/xyz regex and something'; } location ~ /foo/(.*)/bar { return 200 '/foo/ and $1 and /bar and something'; } } none The Location will be matched against the beginning of the requested URI. = Match a Location exactly against the requested URI. ~ Case-Sensitive regular expression match against the requested URI. ^~ Longest non-regular expression match against the requested URI. 
 If the requested URI matches, no further matching will takes place. ~* Case-Insensitive regular expression match against the requested URI. $ curl http://localhost/foo/data/bar
 $ curl http://localhost/foo/data/barry
 $ curl http://localhost/foo/data/bar/abc
  27. Static Files server { listen 80; server_name foo.example.com; root /opt/data/foo/;

    index index.html; location / { try_files $uri $uri/ /index.html; } } server { listen 80; server_name _; location /foo/ { autoindex off; alias /opt/data/foo/; try_files $uri $uri/ /foo/index.html; } } $ curl http://foo.example.com $ curl http://foo.example.com/page/aaa $ curl http://example.com/foo/ $ curl http://example.com/foo/page/aaa
  28. log_format upstream_log '$time_local -> $remote_addr -> $server_name - $upstream_addr -

    ' '$http_user_agent - $bytes_sent/$upstream_bytes_received - "$request" $status - ' '$request_time/$upstream_response_time'; access_log /var/log/nginx/access.log upstream_log buffer=64k flush=5s; log_format logger_json escape=json '{"time": $msec, "address": "$remote_addr", "server_name": "$server_name",' '"upstream_addr": "$upstream_addr", "user_agent": "$http_user_agent",' '"bytes_sent": $bytes_sent, "upstream_bytes_received": $upstream_bytes_received,' '"method": "$request_method", "uri": "$request_uri", "host": "$http_host",' '"status": $status,"request_length": $request_length, "resp_time": $request_time,' '"upstream_response_time": $upstream_response_time }'; access_log /var/log/nginx/access.log logger_json buffer=64k flush=5s; Json Human Logging server { location /demo/ { access_log off; proxy_pass http://my_service; } } Disable Logs On a Specific Path