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

Essential Nginx patterns

Essential Nginx patterns

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